2
0
mirror of https://github.com/edk2-porting/linux-next.git synced 2024-12-22 20:23:57 +08:00

hwmon updates for v4.8

New drivers for FTS BMC "Teutates", TI INA3221, and Sensirion SHT3x.
 Added support for Microchip MCP9808 and TI TMP461.
 Cleanup and minor fixes in various drivers.
 -----BEGIN PGP SIGNATURE-----
 Version: GnuPG v1
 
 iQIcBAABAgAGBQJXlXNKAAoJEMsfJm/On5mB7QwP/06g1PTYkZDtsvPD0kYwjqs4
 4je/OpmGFoSaytTJiOd1tQxfgmHe4m8eOZbTw0MyvvvJaDGSHPEUsEpH8DautTUD
 O4ngVfFV9R4fV6lBunLaXZc92+00pDqKYky5jzsMy/t0TJ43ycRvEt3Q+k9D85Go
 0hjP72CE6eEHzLKlrbDxbyZOEohbJyqO5bQL8FUy0k7+4LxN8FAKNyNgRW0fNwf4
 FSHMUNil0hGNvhApadvEk6uvQoPYpkTxQSXzFldT3zouJVKhgILBmQGNA+Be0bwP
 PS7ALhcVRcTtcZprY6BNf86cujo+5yWLI1Ifqeu+sNQUkwaZk8df/fxF4XpjWrez
 L1HN4p9nCUXhDGpeTxDedTeWZHDBLr2CuPPmm2vWbRM+gl+LJ5CFLq3oqOEcSR43
 bq8+oRgXtJHK0tlWJG3neabbArMV57bhrEsft4OthMFFaNRquZnqZylX7dBlQO+/
 rEGqALwmutHY2BmVM/jP/WQ6SBZTxWmsq/XVhheDqu842oukzH5CijddCL/JUQbr
 aC+u3gmXb2/gquEOgYosRAAAqLL0IH7AjSxXvhLM2lKSgJlJGmBXqezx9E4bYn6o
 RAAG4qlCIt5YJUkQ61r8j9WmlXd/BIzACyVSCk5tau61kGscepPk7Vk0dgdYUXMC
 wcbDIYHdC2p0voaZdXLf
 =O4g9
 -----END PGP SIGNATURE-----

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

Pull hwmon updates from Guenter Roeck:

 - New drivers for FTS BMC "Teutates", TI INA3221, and Sensirion SHT3x.

 - Added support for Microchip MCP9808 and TI TMP461.

 - Cleanup and minor fixes in various drivers.

* tag 'hwmon-for-linus-v4.8' of git://git.kernel.org/pub/scm/linux/kernel/git/groeck/linux-staging: (37 commits)
  Documentation: dtb: xgene: Add hwmon dts binding documentation
  hwmon: (ftsteutates) Remove unused including <linux/version.h>
  hwmon: (adt7411) set bit 3 in CFG1 register
  hwmon: Add driver for FTS BMC chip "Teutates"
  hwmon: (sht3x) add humidity heater element control
  hwmon: (jc42) Add support for generic JC-42.4 devicetree binding
  dt/bindings: Add bindings for JC-42.4 compatible temperature sensors
  hwmon: (tmp102) Convert to use regmap, and drop local cache
  hwmon: (tmp102) Rework chip configuration
  hwmon: (tmp102) Improve handling of initial read delay
  hwmon: (lm90) Drop unnecessary else statements
  hwmon: (lm90) Use bool for valid flag
  hwmon: (lm90) Read limit registers only once
  hwmon: (lm90) Simplify read functions
  hwmon: (lm90) Use devm_hwmon_device_register_with_groups
  hwmon: (lm90) Use devm_add_action for cleanup
  hwmon: (lm75) Convert to use regmap
  hwmon: (lm75) Add update_interval attribute
  hwmon: (lm75) Drop lm75_read_value and lm75_write_value
  hwmon: (lm75) Handle cleanup with devm_add_action
  ...
This commit is contained in:
Linus Torvalds 2016-07-24 21:10:30 -07:00
commit dd95069545
27 changed files with 2965 additions and 607 deletions

View File

@ -0,0 +1,14 @@
APM X-Gene hwmon driver
APM X-Gene SOC sensors are accessed over the "SLIMpro" mailbox.
Required properties :
- compatible : should be "apm,xgene-slimpro-hwmon"
- mboxes : use the label reference for the mailbox as the first parameter.
The second parameter is the channel number.
Example :
hwmonslimpro {
compatible = "apm,xgene-slimpro-hwmon";
mboxes = <&mailbox 7>;
};

View File

@ -0,0 +1,42 @@
Properties for Jedec JC-42.4 compatible temperature sensors
Required properties:
- compatible: May include a device-specific string consisting of the
manufacturer and the name of the chip. A list of supported
chip names follows.
Must include "jedec,jc-42.4-temp" for any Jedec JC-42.4
compatible temperature sensor.
Supported chip names:
adi,adt7408
atmel,at30ts00
atmel,at30tse004
onnn,cat6095
onnn,cat34ts02
maxim,max6604
microchip,mcp9804
microchip,mcp9805
microchip,mcp9808
microchip,mcp98243
microchip,mcp98244
microchip,mcp9843
nxp,se97
nxp,se98
st,stts2002
st,stts2004
st,stts3000
st,stts424
st,stts424e
idt,tse2002
idt,tse2004
idt,ts3000
idt,ts3001
- reg: I2C address
Example:
temp-sensor@1a {
compatible = "jedec,jc-42.4-temp";
reg = <0x1a>;
};

View File

@ -24,7 +24,7 @@ Supported chips:
AW9D-MAX) (2)
1) For revisions 2 and 3 uGuru's the driver can autodetect the
sensortype (Volt or Temp) for bank1 sensors, for revision 1 uGuru's
this doesnot always work. For these uGuru's the autodection can
this does not always work. For these uGuru's the autodetection can
be overridden with the bank1_types module param. For all 3 known
revison 1 motherboards the correct use of this param is:
bank1_types=1,1,0,0,0,0,0,2,0,0,0,0,2,0,0,1

View File

@ -0,0 +1,23 @@
Kernel driver ftsteutates
=====================
Supported chips:
* FTS Teutates
Prefix: 'ftsteutates'
Addresses scanned: I2C 0x73 (7-Bit)
Author: Thilo Cestonaro <thilo.cestonaro@ts.fujitsu.com>
Description
-----------
The BMC Teutates is the Eleventh generation of Superior System
monitoring and thermal management solution. It is builds on the basic
functionality of the BMC Theseus and contains several new features and
enhancements. It can monitor up to 4 voltages, 16 temperatures and
8 fans. It also contains an integrated watchdog which is currently
implemented in this driver.
Specification of the chip can be found here:
ftp:///pub/Mainboard-OEM-Sales/Services/Software&Tools/Linux_SystemMonitoring&Watchdog&GPIO/BMC-Teutates_Specification_V1.21.pdf
ftp:///pub/Mainboard-OEM-Sales/Services/Software&Tools/Linux_SystemMonitoring&Watchdog&GPIO/Fujitsu_mainboards-1-Sensors_HowTo-en-US.pdf

View File

@ -0,0 +1,35 @@
Kernel driver ina3221
=====================
Supported chips:
* Texas Instruments INA3221
Prefix: 'ina3221'
Addresses: I2C 0x40 - 0x43
Datasheet: Publicly available at the Texas Instruments website
http://www.ti.com/
Author: Andrew F. Davis <afd@ti.com>
Description
-----------
The Texas Instruments INA3221 monitors voltage, current, and power on the high
side of up to three D.C. power supplies. The INA3221 monitors both shunt drop
and supply voltage, with programmable conversion times and averaging, current
and power are calculated host-side from these.
Sysfs entries
-------------
in[123]_input Bus voltage(mV) channels
curr[123]_input Current(mA) measurement channels
shunt[123]_resistor Shunt resistance(uOhm) channels
curr[123]_crit Critical alert current(mA) setting, activates the
corresponding alarm when the respective current
is above this value
curr[123]_crit_alarm Critical alert current limit exceeded
curr[123]_max Warning alert current(mA) setting, activates the
corresponding alarm when the respective current
average is above this value.
curr[123]_max_alarm Warning alert current limit exceeded
in[456]_input Shunt voltage(uV) for channels 1, 2, and 3 respectively

View File

@ -18,10 +18,11 @@ Supported chips:
* Maxim MAX6604
Datasheets:
http://datasheets.maxim-ic.com/en/ds/MAX6604.pdf
* Microchip MCP9804, MCP9805, MCP98242, MCP98243, MCP98244, MCP9843
* Microchip MCP9804, MCP9805, MCP9808, MCP98242, MCP98243, MCP98244, MCP9843
Datasheets:
http://ww1.microchip.com/downloads/en/DeviceDoc/22203C.pdf
http://ww1.microchip.com/downloads/en/DeviceDoc/21977b.pdf
http://ww1.microchip.com/downloads/en/DeviceDoc/25095A.pdf
http://ww1.microchip.com/downloads/en/DeviceDoc/21996a.pdf
http://ww1.microchip.com/downloads/en/DeviceDoc/22153c.pdf
http://ww1.microchip.com/downloads/en/DeviceDoc/22327A.pdf

View File

@ -17,7 +17,7 @@ This driver implements support for the Maxim MAX1668, MAX1805 and MAX1989
chips.
The three devices are very similar, but the MAX1805 has a reduced feature
set; only two remote temperature inputs vs the four avaible on the other
set; only two remote temperature inputs vs the four available on the other
two ICs.
The driver is able to distinguish between the devices and creates sysfs

76
Documentation/hwmon/sht3x Normal file
View File

@ -0,0 +1,76 @@
Kernel driver sht3x
===================
Supported chips:
* Sensirion SHT3x-DIS
Prefix: 'sht3x'
Addresses scanned: none
Datasheet: http://www.sensirion.com/fileadmin/user_upload/customers/sensirion/Dokumente/Humidity/Sensirion_Humidity_Datasheet_SHT3x_DIS.pdf
Author:
David Frey <david.frey@sensirion.com>
Pascal Sachs <pascal.sachs@sensirion.com>
Description
-----------
This driver implements support for the Sensirion SHT3x-DIS chip, a humidity
and temperature sensor. Temperature is measured in degrees celsius, relative
humidity is expressed as a percentage. In the sysfs interface, all values are
scaled by 1000, i.e. the value for 31.5 degrees celsius is 31500.
The device communicates with the I2C protocol. Sensors can have the I2C
addresses 0x44 or 0x45, depending on the wiring. See
Documentation/i2c/instantiating-devices for methods to instantiate the device.
There are two options configurable by means of sht3x_platform_data:
1. blocking (pull the I2C clock line down while performing the measurement) or
non-blocking mode. Blocking mode will guarantee the fastest result but
the I2C bus will be busy during that time. By default, non-blocking mode
is used. Make sure clock-stretching works properly on your device if you
want to use blocking mode.
2. high or low accuracy. High accuracy is used by default and using it is
strongly recommended.
The sht3x sensor supports a single shot mode as well as 5 periodic measure
modes, which can be controlled with the update_interval sysfs interface.
The allowed update_interval in milliseconds are as follows:
* 0 single shot mode
* 2000 0.5 Hz periodic measurement
* 1000 1 Hz periodic measurement
* 500 2 Hz periodic measurement
* 250 4 Hz periodic measurement
* 100 10 Hz periodic measurement
In the periodic measure mode, the sensor automatically triggers a measurement
with the configured update interval on the chip. When a temperature or humidity
reading exceeds the configured limits, the alert attribute is set to 1 and
the alert pin on the sensor is set to high.
When the temperature and humidity readings move back between the hysteresis
values, the alert bit is set to 0 and the alert pin on the sensor is set to
low.
sysfs-Interface
---------------
temp1_input: temperature input
humidity1_input: humidity input
temp1_max: temperature max value
temp1_max_hyst: temperature hysteresis value for max limit
humidity1_max: humidity max value
humidity1_max_hyst: humidity hysteresis value for max limit
temp1_min: temperature min value
temp1_min_hyst: temperature hysteresis value for min limit
humidity1_min: humidity min value
humidity1_min_hyst: humidity hysteresis value for min limit
temp1_alarm: alarm flag is set to 1 if the temperature is outside the
configured limits. Alarm only works in periodic measure mode
humidity1_alarm: alarm flag is set to 1 if the humidity is outside the
configured limits. Alarm only works in periodic measure mode
heater_enable: heater enable, heating element removes excess humidity from
sensor
0: turned off
1: turned on
update_interval: update interval, 0 for single shot, interval in msec
for periodic measurement. If the interval is not supported
by the sensor, the next faster interval is chosen

View File

@ -15,10 +15,15 @@ increase the chances of your change being accepted.
Documentation/SubmittingPatches
Documentation/CodingStyle
* If your patch generates checkpatch warnings, please refrain from explanations
such as "I don't like that coding style". Keep in mind that each unnecessary
warning helps hiding a real problem. If you don't like the kernel coding
style, don't write kernel drivers.
* Please run your patch through 'checkpatch --strict'. There should be no
errors, no warnings, and few if any check messages. If there are any
messages, please be prepared to explain.
* If your patch generates checkpatch errors, warnings, or check messages,
please refrain from explanations such as "I prefer that coding style".
Keep in mind that each unnecessary message helps hiding a real problem,
and a consistent coding style makes it easier for others to understand
and review the code.
* Please test your patch thoroughly. We are not your test group.
Sometimes a patch can not or not completely be tested because of missing
@ -61,15 +66,30 @@ increase the chances of your change being accepted.
* Make sure that all dependencies are listed in Kconfig.
* Please list include files in alphabetic order.
* Please align continuation lines with '(' on the previous line.
* Avoid forward declarations if you can. Rearrange the code if necessary.
* Avoid macros to generate groups of sensor attributes. It not only confuses
checkpatch, but also makes it more difficult to review the code.
* Avoid calculations in macros and macro-generated functions. While such macros
may save a line or so in the source, it obfuscates the code and makes code
review more difficult. It may also result in code which is more complicated
than necessary. Use inline functions or just regular functions instead.
* Limit the number of kernel log messages. In general, your driver should not
generate an error message just because a runtime operation failed. Report
errors to user space instead, using an appropriate error code. Keep in mind
that kernel error log messages not only fill up the kernel log, but also are
printed synchronously, most likely with interrupt disabled, often to a serial
console. Excessive logging can seriously affect system performance.
* Use devres functions whenever possible to allocate resources. For rationale
and supported functions, please see Documentation/driver-model/devres.txt.
If a function is not supported by devres, consider using devm_add_action().
* If the driver has a detect function, make sure it is silent. Debug messages
and messages printed after a successful detection are acceptable, but it
@ -96,8 +116,16 @@ increase the chances of your change being accepted.
writing to it might cause a bad misconfiguration.
* Make sure there are no race conditions in the probe function. Specifically,
completely initialize your chip first, then create sysfs entries and register
with the hwmon subsystem.
completely initialize your chip and your driver first, then register with
the hwmon subsystem.
* Use devm_hwmon_device_register_with_groups() or, if your driver needs a remove
function, hwmon_device_register_with_groups() to register your driver with the
hwmon subsystem. Try using devm_add_action() instead of a remove function if
possible. Do not use hwmon_device_register().
* Your driver should be buildable as module. If not, please be prepared to
explain why it has to be built into the kernel.
* Do not provide support for deprecated sysfs attributes.

View File

@ -22,6 +22,9 @@ Supported chips:
Prefix: 'tmp435'
Addresses scanned: I2C 0x48 - 0x4f
Datasheet: http://focus.ti.com/docs/prod/folders/print/tmp435.html
* Texas Instruments TMP461
Prefix: 'tmp461'
Datasheet: http://www.ti.com/product/tmp461
Authors:
Hans de Goede <hdegoede@redhat.com>
@ -31,8 +34,8 @@ Description
-----------
This driver implements support for Texas Instruments TMP401, TMP411,
TMP431, TMP432 and TMP435 chips. These chips implement one or two remote
and one local temperature sensors. Temperature is measured in degrees
TMP431, TMP432, TMP435, and TMP461 chips. These chips implement one or two
remote and one local temperature sensors. Temperature is measured in degrees
Celsius. Resolution of the remote sensor is 0.0625 degree. Local
sensor resolution can be set to 0.5, 0.25, 0.125 or 0.0625 degree (not
supported by the driver so far, so using the default resolution of 0.5
@ -55,3 +58,10 @@ some additional features.
TMP432 is compatible with TMP401 and TMP431. It supports two external
temperature sensors.
TMP461 is compatible with TMP401. It supports offset correction
that is applied to the remote sensor.
* Sensor offset values are temperature values
Exported via sysfs attribute tempX_offset

View File

@ -486,6 +486,18 @@ config SENSORS_FSCHMD
This driver can also be built as a module. If so, the module
will be called fschmd.
config SENSORS_FTSTEUTATES
tristate "Fujitsu Technology Solutions sensor chip Teutates"
depends on I2C && WATCHDOG
select WATCHDOG_CORE
help
If you say yes here you get support for the Fujitsu Technology
Solutions (FTS) sensor chip "Teutates" including support for
the integrated watchdog.
This driver can also be built as a module. If so, the module
will be called ftsteutates.
config SENSORS_GL518SM
tristate "Genesys Logic GL518SM"
depends on I2C
@ -645,8 +657,8 @@ config SENSORS_JC42
temperature sensors, which are used on many DDR3 memory modules for
mobile devices and servers. Support will include, but not be limited
to, ADT7408, AT30TS00, CAT34TS02, CAT6095, MAX6604, MCP9804, MCP9805,
MCP98242, MCP98243, MCP98244, MCP9843, SE97, SE98, STTS424(E),
STTS2002, STTS3000, TSE2002, TSE2004, TS3000, and TS3001.
MCP9808, MCP98242, MCP98243, MCP98244, MCP9843, SE97, SE98,
STTS424(E), STTS2002, STTS3000, TSE2002, TSE2004, TS3000, and TS3001.
This driver can also be built as a module. If so, the module
will be called jc42.
@ -958,6 +970,7 @@ config SENSORS_LM75
tristate "National Semiconductor LM75 and compatibles"
depends on I2C
depends on THERMAL || !THERMAL_OF
select REGMAP_I2C
help
If you say yes here you get support for one common type of
temperature sensor chip, with models including:
@ -1265,6 +1278,17 @@ config SENSORS_SHT21
This driver can also be built as a module. If so, the module
will be called sht21.
config SENSORS_SHT3x
tristate "Sensiron humidity and temperature sensors. SHT3x and compat."
depends on I2C
select CRC8
help
If you say yes here you get support for the Sensiron SHT30 and SHT31
humidity and temperature sensors.
This driver can also be built as a module. If so, the module
will be called sht3x.
config SENSORS_SHTC1
tristate "Sensiron humidity and temperature sensors. SHTC1 and compat."
depends on I2C
@ -1514,6 +1538,17 @@ config SENSORS_INA2XX
This driver can also be built as a module. If so, the module
will be called ina2xx.
config SENSORS_INA3221
tristate "Texas Instruments INA3221 Triple Power Monitor"
depends on I2C
select REGMAP_I2C
help
If you say yes here you get support for the TI INA3221 Triple Power
Monitor.
This driver can also be built as a module. If so, the module
will be called ina3221.
config SENSORS_TC74
tristate "Microchip TC74"
depends on I2C
@ -1538,6 +1573,7 @@ config SENSORS_TMP102
tristate "Texas Instruments TMP102"
depends on I2C
depends on THERMAL || !THERMAL_OF
select REGMAP_I2C
help
If you say yes here you get support for Texas Instruments TMP102
sensor chips.
@ -1561,7 +1597,7 @@ config SENSORS_TMP401
depends on I2C
help
If you say yes here you get support for Texas Instruments TMP401,
TMP411, TMP431, TMP432 and TMP435 temperature sensor chips.
TMP411, TMP431, TMP432, TMP435, and TMP461 temperature sensor chips.
This driver can also be built as a module. If so, the module
will be called tmp401.

View File

@ -62,6 +62,7 @@ obj-$(CONFIG_SENSORS_F71882FG) += f71882fg.o
obj-$(CONFIG_SENSORS_F75375S) += f75375s.o
obj-$(CONFIG_SENSORS_FAM15H_POWER) += fam15h_power.o
obj-$(CONFIG_SENSORS_FSCHMD) += fschmd.o
obj-$(CONFIG_SENSORS_FTSTEUTATES) += ftsteutates.o
obj-$(CONFIG_SENSORS_G760A) += g760a.o
obj-$(CONFIG_SENSORS_G762) += g762.o
obj-$(CONFIG_SENSORS_GL518SM) += gl518sm.o
@ -77,6 +78,7 @@ obj-$(CONFIG_SENSORS_IBMPOWERNV)+= ibmpowernv.o
obj-$(CONFIG_SENSORS_IIO_HWMON) += iio_hwmon.o
obj-$(CONFIG_SENSORS_INA209) += ina209.o
obj-$(CONFIG_SENSORS_INA2XX) += ina2xx.o
obj-$(CONFIG_SENSORS_INA3221) += ina3221.o
obj-$(CONFIG_SENSORS_IT87) += it87.o
obj-$(CONFIG_SENSORS_JC42) += jc42.o
obj-$(CONFIG_SENSORS_JZ4740) += jz4740-hwmon.o
@ -138,6 +140,7 @@ obj-$(CONFIG_SENSORS_SCH5627) += sch5627.o
obj-$(CONFIG_SENSORS_SCH5636) += sch5636.o
obj-$(CONFIG_SENSORS_SHT15) += sht15.o
obj-$(CONFIG_SENSORS_SHT21) += sht21.o
obj-$(CONFIG_SENSORS_SHT3x) += sht3x.o
obj-$(CONFIG_SENSORS_SHTC1) += shtc1.o
obj-$(CONFIG_SENSORS_SIS5595) += sis5595.o
obj-$(CONFIG_SENSORS_SMM665) += smm665.o

View File

@ -37,7 +37,6 @@ enum ad7314_variant {
struct ad7314_data {
struct spi_device *spi_dev;
struct device *hwmon_dev;
u16 rx ____cacheline_aligned;
};
@ -88,62 +87,30 @@ static ssize_t ad7314_show_temperature(struct device *dev,
}
}
static ssize_t ad7314_show_name(struct device *dev,
struct device_attribute *devattr, char *buf)
{
return sprintf(buf, "%s\n", to_spi_device(dev)->modalias);
}
static DEVICE_ATTR(name, S_IRUGO, ad7314_show_name, NULL);
static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO,
ad7314_show_temperature, NULL, 0);
static struct attribute *ad7314_attributes[] = {
&dev_attr_name.attr,
static struct attribute *ad7314_attrs[] = {
&sensor_dev_attr_temp1_input.dev_attr.attr,
NULL,
};
static const struct attribute_group ad7314_group = {
.attrs = ad7314_attributes,
};
ATTRIBUTE_GROUPS(ad7314);
static int ad7314_probe(struct spi_device *spi_dev)
{
int ret;
struct ad7314_data *chip;
struct device *hwmon_dev;
chip = devm_kzalloc(&spi_dev->dev, sizeof(*chip), GFP_KERNEL);
if (chip == NULL)
return -ENOMEM;
spi_set_drvdata(spi_dev, chip);
ret = sysfs_create_group(&spi_dev->dev.kobj, &ad7314_group);
if (ret < 0)
return ret;
chip->hwmon_dev = hwmon_device_register(&spi_dev->dev);
if (IS_ERR(chip->hwmon_dev)) {
ret = PTR_ERR(chip->hwmon_dev);
goto error_remove_group;
}
chip->spi_dev = spi_dev;
return 0;
error_remove_group:
sysfs_remove_group(&spi_dev->dev.kobj, &ad7314_group);
return ret;
}
static int ad7314_remove(struct spi_device *spi_dev)
{
struct ad7314_data *chip = spi_get_drvdata(spi_dev);
hwmon_device_unregister(chip->hwmon_dev);
sysfs_remove_group(&spi_dev->dev.kobj, &ad7314_group);
return 0;
hwmon_dev = devm_hwmon_device_register_with_groups(&spi_dev->dev,
spi_dev->modalias,
chip, ad7314_groups);
return PTR_ERR_OR_ZERO(hwmon_dev);
}
static const struct spi_device_id ad7314_id[] = {
@ -159,7 +126,6 @@ static struct spi_driver ad7314_driver = {
.name = "ad7314",
},
.probe = ad7314_probe,
.remove = ad7314_remove,
.id_table = ad7314_id,
};

View File

@ -66,14 +66,12 @@
#include <linux/hwmon.h>
#include <linux/hwmon-sysfs.h>
#include <linux/err.h>
#include <linux/mutex.h>
#include <linux/delay.h>
#define DEVICE_NAME "ads7871"
struct ads7871_data {
struct device *hwmon_dev;
struct mutex update_lock;
struct spi_device *spi;
};
static int ads7871_read_reg8(struct spi_device *spi, int reg)
@ -101,7 +99,8 @@ static int ads7871_write_reg8(struct spi_device *spi, int reg, u8 val)
static ssize_t show_voltage(struct device *dev,
struct device_attribute *da, char *buf)
{
struct spi_device *spi = to_spi_device(dev);
struct ads7871_data *pdata = dev_get_drvdata(dev);
struct spi_device *spi = pdata->spi;
struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
int ret, val, i = 0;
uint8_t channel, mux_cnv;
@ -139,12 +138,6 @@ static ssize_t show_voltage(struct device *dev,
}
}
static ssize_t ads7871_show_name(struct device *dev,
struct device_attribute *devattr, char *buf)
{
return sprintf(buf, "%s\n", to_spi_device(dev)->modalias);
}
static SENSOR_DEVICE_ATTR(in0_input, S_IRUGO, show_voltage, NULL, 0);
static SENSOR_DEVICE_ATTR(in1_input, S_IRUGO, show_voltage, NULL, 1);
static SENSOR_DEVICE_ATTR(in2_input, S_IRUGO, show_voltage, NULL, 2);
@ -154,9 +147,7 @@ static SENSOR_DEVICE_ATTR(in5_input, S_IRUGO, show_voltage, NULL, 5);
static SENSOR_DEVICE_ATTR(in6_input, S_IRUGO, show_voltage, NULL, 6);
static SENSOR_DEVICE_ATTR(in7_input, S_IRUGO, show_voltage, NULL, 7);
static DEVICE_ATTR(name, S_IRUGO, ads7871_show_name, NULL);
static struct attribute *ads7871_attributes[] = {
static struct attribute *ads7871_attrs[] = {
&sensor_dev_attr_in0_input.dev_attr.attr,
&sensor_dev_attr_in1_input.dev_attr.attr,
&sensor_dev_attr_in2_input.dev_attr.attr,
@ -165,21 +156,18 @@ static struct attribute *ads7871_attributes[] = {
&sensor_dev_attr_in5_input.dev_attr.attr,
&sensor_dev_attr_in6_input.dev_attr.attr,
&sensor_dev_attr_in7_input.dev_attr.attr,
&dev_attr_name.attr,
NULL
};
static const struct attribute_group ads7871_group = {
.attrs = ads7871_attributes,
};
ATTRIBUTE_GROUPS(ads7871);
static int ads7871_probe(struct spi_device *spi)
{
int ret, err;
struct device *dev = &spi->dev;
int ret;
uint8_t val;
struct ads7871_data *pdata;
dev_dbg(&spi->dev, "probe\n");
struct device *hwmon_dev;
/* Configure the SPI bus */
spi->mode = (SPI_MODE_0);
@ -193,7 +181,7 @@ static int ads7871_probe(struct spi_device *spi)
ads7871_write_reg8(spi, REG_OSC_CONTROL, val);
ret = ads7871_read_reg8(spi, REG_OSC_CONTROL);
dev_dbg(&spi->dev, "REG_OSC_CONTROL write:%x, read:%x\n", val, ret);
dev_dbg(dev, "REG_OSC_CONTROL write:%x, read:%x\n", val, ret);
/*
* because there is no other error checking on an SPI bus
* we need to make sure we really have a chip
@ -201,46 +189,23 @@ static int ads7871_probe(struct spi_device *spi)
if (val != ret)
return -ENODEV;
pdata = devm_kzalloc(&spi->dev, sizeof(struct ads7871_data),
GFP_KERNEL);
pdata = devm_kzalloc(dev, sizeof(struct ads7871_data), GFP_KERNEL);
if (!pdata)
return -ENOMEM;
err = sysfs_create_group(&spi->dev.kobj, &ads7871_group);
if (err < 0)
return err;
pdata->spi = spi;
spi_set_drvdata(spi, pdata);
pdata->hwmon_dev = hwmon_device_register(&spi->dev);
if (IS_ERR(pdata->hwmon_dev)) {
err = PTR_ERR(pdata->hwmon_dev);
goto error_remove;
}
return 0;
error_remove:
sysfs_remove_group(&spi->dev.kobj, &ads7871_group);
return err;
}
static int ads7871_remove(struct spi_device *spi)
{
struct ads7871_data *pdata = spi_get_drvdata(spi);
hwmon_device_unregister(pdata->hwmon_dev);
sysfs_remove_group(&spi->dev.kobj, &ads7871_group);
return 0;
hwmon_dev = devm_hwmon_device_register_with_groups(dev, spi->modalias,
pdata,
ads7871_groups);
return PTR_ERR_OR_ZERO(hwmon_dev);
}
static struct spi_driver ads7871_driver = {
.driver = {
.name = DEVICE_NAME,
},
.probe = ads7871_probe,
.remove = ads7871_remove,
};
module_spi_driver(ads7871_driver);

View File

@ -30,6 +30,7 @@
#define ADT7411_REG_CFG1 0x18
#define ADT7411_CFG1_START_MONITOR (1 << 0)
#define ADT7411_CFG1_RESERVED_BIT3 (1 << 3)
#define ADT7411_REG_CFG2 0x19
#define ADT7411_CFG2_DISABLE_AVG (1 << 5)
@ -296,8 +297,10 @@ static int adt7411_probe(struct i2c_client *client,
mutex_init(&data->device_lock);
mutex_init(&data->update_lock);
/* According to the datasheet, we must only write 1 to bit 3 */
ret = adt7411_modify_bit(client, ADT7411_REG_CFG1,
ADT7411_CFG1_START_MONITOR, 1);
ADT7411_CFG1_RESERVED_BIT3
| ADT7411_CFG1_START_MONITOR, 1);
if (ret < 0)
return ret;

View File

@ -81,6 +81,7 @@ static bool disallow_fan_type_call;
#define I8K_HWMON_HAVE_TEMP4 (1 << 3)
#define I8K_HWMON_HAVE_FAN1 (1 << 4)
#define I8K_HWMON_HAVE_FAN2 (1 << 5)
#define I8K_HWMON_HAVE_FAN3 (1 << 6)
MODULE_AUTHOR("Massimo Dal Zotto (dz@debian.org)");
MODULE_AUTHOR("Pali Rohár <pali.rohar@gmail.com>");
@ -139,6 +140,14 @@ static int i8k_smm(struct smm_regs *regs)
int eax = regs->eax;
cpumask_var_t old_mask;
#ifdef DEBUG
int ebx = regs->ebx;
unsigned long duration;
ktime_t calltime, delta, rettime;
calltime = ktime_get();
#endif
/* SMM requires CPU 0 */
if (!alloc_cpumask_var(&old_mask, GFP_KERNEL))
return -ENOMEM;
@ -210,6 +219,15 @@ static int i8k_smm(struct smm_regs *regs)
out:
set_cpus_allowed_ptr(current, old_mask);
free_cpumask_var(old_mask);
#ifdef DEBUG
rettime = ktime_get();
delta = ktime_sub(rettime, calltime);
duration = ktime_to_ns(delta) >> 10;
pr_debug("smm(0x%.4x 0x%.4x) = 0x%.4x (took %7lu usecs)\n", eax, ebx,
(rc ? 0xffff : regs->eax & 0xffff), duration);
#endif
return rc;
}
@ -252,7 +270,7 @@ static int _i8k_get_fan_type(int fan)
static int i8k_get_fan_type(int fan)
{
/* I8K_SMM_GET_FAN_TYPE SMM call is expensive, so cache values */
static int types[2] = { INT_MIN, INT_MIN };
static int types[3] = { INT_MIN, INT_MIN, INT_MIN };
if (types[fan] == INT_MIN)
types[fan] = _i8k_get_fan_type(fan);
@ -719,6 +737,12 @@ static SENSOR_DEVICE_ATTR(fan2_label, S_IRUGO, i8k_hwmon_show_fan_label, NULL,
1);
static SENSOR_DEVICE_ATTR(pwm2, S_IRUGO | S_IWUSR, i8k_hwmon_show_pwm,
i8k_hwmon_set_pwm, 1);
static SENSOR_DEVICE_ATTR(fan3_input, S_IRUGO, i8k_hwmon_show_fan, NULL,
2);
static SENSOR_DEVICE_ATTR(fan3_label, S_IRUGO, i8k_hwmon_show_fan_label, NULL,
2);
static SENSOR_DEVICE_ATTR(pwm3, S_IRUGO | S_IWUSR, i8k_hwmon_show_pwm,
i8k_hwmon_set_pwm, 2);
static struct attribute *i8k_attrs[] = {
&sensor_dev_attr_temp1_input.dev_attr.attr, /* 0 */
@ -735,6 +759,9 @@ static struct attribute *i8k_attrs[] = {
&sensor_dev_attr_fan2_input.dev_attr.attr, /* 11 */
&sensor_dev_attr_fan2_label.dev_attr.attr, /* 12 */
&sensor_dev_attr_pwm2.dev_attr.attr, /* 13 */
&sensor_dev_attr_fan3_input.dev_attr.attr, /* 14 */
&sensor_dev_attr_fan3_label.dev_attr.attr, /* 15 */
&sensor_dev_attr_pwm3.dev_attr.attr, /* 16 */
NULL
};
@ -742,7 +769,7 @@ static umode_t i8k_is_visible(struct kobject *kobj, struct attribute *attr,
int index)
{
if (disallow_fan_type_call &&
(index == 9 || index == 12))
(index == 9 || index == 12 || index == 15))
return 0;
if (index >= 0 && index <= 1 &&
!(i8k_hwmon_flags & I8K_HWMON_HAVE_TEMP1))
@ -762,6 +789,9 @@ static umode_t i8k_is_visible(struct kobject *kobj, struct attribute *attr,
if (index >= 11 && index <= 13 &&
!(i8k_hwmon_flags & I8K_HWMON_HAVE_FAN2))
return 0;
if (index >= 14 && index <= 16 &&
!(i8k_hwmon_flags & I8K_HWMON_HAVE_FAN3))
return 0;
return attr->mode;
}
@ -807,6 +837,13 @@ static int __init i8k_init_hwmon(void)
if (err >= 0)
i8k_hwmon_flags |= I8K_HWMON_HAVE_FAN2;
/* Third fan attributes, if fan status or type is OK */
err = i8k_get_fan_status(2);
if (err < 0)
err = i8k_get_fan_type(2);
if (err >= 0)
i8k_hwmon_flags |= I8K_HWMON_HAVE_FAN3;
i8k_hwmon_dev = hwmon_device_register_with_groups(NULL, "dell_smm",
NULL, i8k_groups);
if (IS_ERR(i8k_hwmon_dev)) {

View File

@ -464,7 +464,7 @@ static int emc6w201_detect(struct i2c_client *client,
if (verstep < 0 || (verstep & 0xF0) != 0xB0)
return -ENODEV;
if ((verstep & 0x0F) > 2) {
dev_dbg(&client->dev, "Unknwown EMC6W201 stepping %d\n",
dev_dbg(&client->dev, "Unknown EMC6W201 stepping %d\n",
verstep & 0x0F);
return -ENODEV;
}

819
drivers/hwmon/ftsteutates.c Normal file
View File

@ -0,0 +1,819 @@
/*
* Support for the FTS Systemmonitoring Chip "Teutates"
*
* Copyright (C) 2016 Fujitsu Technology Solutions GmbH,
* Thilo Cestonaro <thilo.cestonaro@ts.fujitsu.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*/
#include <linux/err.h>
#include <linux/fs.h>
#include <linux/hwmon.h>
#include <linux/hwmon-sysfs.h>
#include <linux/i2c.h>
#include <linux/init.h>
#include <linux/jiffies.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/slab.h>
#include <linux/sysfs.h>
#include <linux/uaccess.h>
#include <linux/watchdog.h>
#define FTS_DEVICE_ID_REG 0x0000
#define FTS_DEVICE_REVISION_REG 0x0001
#define FTS_DEVICE_STATUS_REG 0x0004
#define FTS_SATELLITE_STATUS_REG 0x0005
#define FTS_EVENT_STATUS_REG 0x0006
#define FTS_GLOBAL_CONTROL_REG 0x0007
#define FTS_SENSOR_EVENT_REG 0x0010
#define FTS_FAN_EVENT_REG 0x0014
#define FTS_FAN_PRESENT_REG 0x0015
#define FTS_POWER_ON_TIME_COUNTER_A 0x007A
#define FTS_POWER_ON_TIME_COUNTER_B 0x007B
#define FTS_POWER_ON_TIME_COUNTER_C 0x007C
#define FTS_PAGE_SELECT_REG 0x007F
#define FTS_WATCHDOG_TIME_PRESET 0x000B
#define FTS_WATCHDOG_CONTROL 0x5081
#define FTS_NO_FAN_SENSORS 0x08
#define FTS_NO_TEMP_SENSORS 0x10
#define FTS_NO_VOLT_SENSORS 0x04
static struct i2c_device_id fts_id[] = {
{ "ftsteutates", 0 },
{ }
};
MODULE_DEVICE_TABLE(i2c, fts_id);
enum WATCHDOG_RESOLUTION {
seconds = 1,
minutes = 60
};
struct fts_data {
struct i2c_client *client;
/* update sensor data lock */
struct mutex update_lock;
/* read/write register lock */
struct mutex access_lock;
unsigned long last_updated; /* in jiffies */
struct watchdog_device wdd;
enum WATCHDOG_RESOLUTION resolution;
bool valid; /* false until following fields are valid */
u8 volt[FTS_NO_VOLT_SENSORS];
u8 temp_input[FTS_NO_TEMP_SENSORS];
u8 temp_alarm;
u8 fan_present;
u8 fan_input[FTS_NO_FAN_SENSORS]; /* in rps */
u8 fan_source[FTS_NO_FAN_SENSORS];
u8 fan_alarm;
};
#define FTS_REG_FAN_INPUT(idx) ((idx) + 0x20)
#define FTS_REG_FAN_SOURCE(idx) ((idx) + 0x30)
#define FTS_REG_FAN_CONTROL(idx) (((idx) << 16) + 0x4881)
#define FTS_REG_TEMP_INPUT(idx) ((idx) + 0x40)
#define FTS_REG_TEMP_CONTROL(idx) (((idx) << 16) + 0x0681)
#define FTS_REG_VOLT(idx) ((idx) + 0x18)
/*****************************************************************************/
/* I2C Helper functions */
/*****************************************************************************/
static int fts_read_byte(struct i2c_client *client, unsigned short reg)
{
int ret;
unsigned char page = reg >> 8;
struct fts_data *data = dev_get_drvdata(&client->dev);
mutex_lock(&data->access_lock);
dev_dbg(&client->dev, "page select - page: 0x%.02x\n", page);
ret = i2c_smbus_write_byte_data(client, FTS_PAGE_SELECT_REG, page);
if (ret < 0)
goto error;
reg &= 0xFF;
ret = i2c_smbus_read_byte_data(client, reg);
dev_dbg(&client->dev, "read - reg: 0x%.02x: val: 0x%.02x\n", reg, ret);
error:
mutex_unlock(&data->access_lock);
return ret;
}
static int fts_write_byte(struct i2c_client *client, unsigned short reg,
unsigned char value)
{
int ret;
unsigned char page = reg >> 8;
struct fts_data *data = dev_get_drvdata(&client->dev);
mutex_lock(&data->access_lock);
dev_dbg(&client->dev, "page select - page: 0x%.02x\n", page);
ret = i2c_smbus_write_byte_data(client, FTS_PAGE_SELECT_REG, page);
if (ret < 0)
goto error;
reg &= 0xFF;
dev_dbg(&client->dev,
"write - reg: 0x%.02x: val: 0x%.02x\n", reg, value);
ret = i2c_smbus_write_byte_data(client, reg, value);
error:
mutex_unlock(&data->access_lock);
return ret;
}
/*****************************************************************************/
/* Data Updater Helper function */
/*****************************************************************************/
static int fts_update_device(struct fts_data *data)
{
int i;
int err = 0;
mutex_lock(&data->update_lock);
if (!time_after(jiffies, data->last_updated + 2 * HZ) && data->valid)
goto exit;
err = fts_read_byte(data->client, FTS_DEVICE_STATUS_REG);
if (err < 0)
goto exit;
data->valid = !!(err & 0x02); /* Data not ready yet */
if (unlikely(!data->valid)) {
err = -EAGAIN;
goto exit;
}
err = fts_read_byte(data->client, FTS_FAN_PRESENT_REG);
if (err < 0)
goto exit;
data->fan_present = err;
err = fts_read_byte(data->client, FTS_FAN_EVENT_REG);
if (err < 0)
goto exit;
data->fan_alarm = err;
for (i = 0; i < FTS_NO_FAN_SENSORS; i++) {
if (data->fan_present & BIT(i)) {
err = fts_read_byte(data->client, FTS_REG_FAN_INPUT(i));
if (err < 0)
goto exit;
data->fan_input[i] = err;
err = fts_read_byte(data->client,
FTS_REG_FAN_SOURCE(i));
if (err < 0)
goto exit;
data->fan_source[i] = err;
} else {
data->fan_input[i] = 0;
data->fan_source[i] = 0;
}
}
err = fts_read_byte(data->client, FTS_SENSOR_EVENT_REG);
if (err < 0)
goto exit;
data->temp_alarm = err;
for (i = 0; i < FTS_NO_TEMP_SENSORS; i++) {
err = fts_read_byte(data->client, FTS_REG_TEMP_INPUT(i));
if (err < 0)
goto exit;
data->temp_input[i] = err;
}
for (i = 0; i < FTS_NO_VOLT_SENSORS; i++) {
err = fts_read_byte(data->client, FTS_REG_VOLT(i));
if (err < 0)
goto exit;
data->volt[i] = err;
}
data->last_updated = jiffies;
err = 0;
exit:
mutex_unlock(&data->update_lock);
return err;
}
/*****************************************************************************/
/* Watchdog functions */
/*****************************************************************************/
static int fts_wd_set_resolution(struct fts_data *data,
enum WATCHDOG_RESOLUTION resolution)
{
int ret;
if (data->resolution == resolution)
return 0;
ret = fts_read_byte(data->client, FTS_WATCHDOG_CONTROL);
if (ret < 0)
return ret;
if ((resolution == seconds && ret & BIT(1)) ||
(resolution == minutes && (ret & BIT(1)) == 0)) {
data->resolution = resolution;
return 0;
}
if (resolution == seconds)
set_bit(1, (unsigned long *)&ret);
else
ret &= ~BIT(1);
ret = fts_write_byte(data->client, FTS_WATCHDOG_CONTROL, ret);
if (ret < 0)
return ret;
data->resolution = resolution;
return ret;
}
static int fts_wd_set_timeout(struct watchdog_device *wdd, unsigned int timeout)
{
struct fts_data *data;
enum WATCHDOG_RESOLUTION resolution = seconds;
int ret;
data = watchdog_get_drvdata(wdd);
/* switch watchdog resolution to minutes if timeout does not fit
* into a byte
*/
if (timeout > 0xFF) {
timeout = DIV_ROUND_UP(timeout, 60) * 60;
resolution = minutes;
}
ret = fts_wd_set_resolution(data, resolution);
if (ret < 0)
return ret;
wdd->timeout = timeout;
return 0;
}
static int fts_wd_start(struct watchdog_device *wdd)
{
struct fts_data *data = watchdog_get_drvdata(wdd);
return fts_write_byte(data->client, FTS_WATCHDOG_TIME_PRESET,
wdd->timeout / (u8)data->resolution);
}
static int fts_wd_stop(struct watchdog_device *wdd)
{
struct fts_data *data;
data = watchdog_get_drvdata(wdd);
return fts_write_byte(data->client, FTS_WATCHDOG_TIME_PRESET, 0);
}
static const struct watchdog_info fts_wd_info = {
.options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE,
.identity = "FTS Teutates Hardware Watchdog",
};
static const struct watchdog_ops fts_wd_ops = {
.owner = THIS_MODULE,
.start = fts_wd_start,
.stop = fts_wd_stop,
.set_timeout = fts_wd_set_timeout,
};
static int fts_watchdog_init(struct fts_data *data)
{
int timeout, ret;
watchdog_set_drvdata(&data->wdd, data);
timeout = fts_read_byte(data->client, FTS_WATCHDOG_TIME_PRESET);
if (timeout < 0)
return timeout;
/* watchdog not running, set timeout to a default of 60 sec. */
if (timeout == 0) {
ret = fts_wd_set_resolution(data, seconds);
if (ret < 0)
return ret;
data->wdd.timeout = 60;
} else {
ret = fts_read_byte(data->client, FTS_WATCHDOG_CONTROL);
if (ret < 0)
return ret;
data->resolution = ret & BIT(1) ? seconds : minutes;
data->wdd.timeout = timeout * (u8)data->resolution;
set_bit(WDOG_HW_RUNNING, &data->wdd.status);
}
/* Register our watchdog part */
data->wdd.info = &fts_wd_info;
data->wdd.ops = &fts_wd_ops;
data->wdd.parent = &data->client->dev;
data->wdd.min_timeout = 1;
/* max timeout 255 minutes. */
data->wdd.max_hw_heartbeat_ms = 0xFF * 60 * MSEC_PER_SEC;
return watchdog_register_device(&data->wdd);
}
/*****************************************************************************/
/* SysFS handler functions */
/*****************************************************************************/
static ssize_t show_in_value(struct device *dev,
struct device_attribute *devattr, char *buf)
{
struct fts_data *data = dev_get_drvdata(dev);
int index = to_sensor_dev_attr(devattr)->index;
int err;
err = fts_update_device(data);
if (err < 0)
return err;
return sprintf(buf, "%u\n", data->volt[index]);
}
static ssize_t show_temp_value(struct device *dev,
struct device_attribute *devattr, char *buf)
{
struct fts_data *data = dev_get_drvdata(dev);
int index = to_sensor_dev_attr(devattr)->index;
int err;
err = fts_update_device(data);
if (err < 0)
return err;
return sprintf(buf, "%u\n", data->temp_input[index]);
}
static ssize_t show_temp_fault(struct device *dev,
struct device_attribute *devattr, char *buf)
{
struct fts_data *data = dev_get_drvdata(dev);
int index = to_sensor_dev_attr(devattr)->index;
int err;
err = fts_update_device(data);
if (err < 0)
return err;
/* 00h Temperature = Sensor Error */
return sprintf(buf, "%d\n", data->temp_input[index] == 0);
}
static ssize_t show_temp_alarm(struct device *dev,
struct device_attribute *devattr, char *buf)
{
struct fts_data *data = dev_get_drvdata(dev);
int index = to_sensor_dev_attr(devattr)->index;
int err;
err = fts_update_device(data);
if (err < 0)
return err;
return sprintf(buf, "%u\n", !!(data->temp_alarm & BIT(index)));
}
static ssize_t
clear_temp_alarm(struct device *dev, struct device_attribute *devattr,
const char *buf, size_t count)
{
struct fts_data *data = dev_get_drvdata(dev);
int index = to_sensor_dev_attr(devattr)->index;
long ret;
ret = fts_update_device(data);
if (ret < 0)
return ret;
if (kstrtoul(buf, 10, &ret) || ret != 0)
return -EINVAL;
mutex_lock(&data->update_lock);
ret = fts_read_byte(data->client, FTS_REG_TEMP_CONTROL(index));
if (ret < 0)
goto error;
ret = fts_write_byte(data->client, FTS_REG_TEMP_CONTROL(index),
ret | 0x1);
if (ret < 0)
goto error;
data->valid = false;
error:
mutex_unlock(&data->update_lock);
return ret;
}
static ssize_t show_fan_value(struct device *dev,
struct device_attribute *devattr, char *buf)
{
struct fts_data *data = dev_get_drvdata(dev);
int index = to_sensor_dev_attr(devattr)->index;
int err;
err = fts_update_device(data);
if (err < 0)
return err;
return sprintf(buf, "%u\n", data->fan_input[index]);
}
static ssize_t show_fan_source(struct device *dev,
struct device_attribute *devattr, char *buf)
{
struct fts_data *data = dev_get_drvdata(dev);
int index = to_sensor_dev_attr(devattr)->index;
int err;
err = fts_update_device(data);
if (err < 0)
return err;
return sprintf(buf, "%u\n", data->fan_source[index]);
}
static ssize_t show_fan_alarm(struct device *dev,
struct device_attribute *devattr, char *buf)
{
struct fts_data *data = dev_get_drvdata(dev);
int index = to_sensor_dev_attr(devattr)->index;
int err;
err = fts_update_device(data);
if (err < 0)
return err;
return sprintf(buf, "%d\n", !!(data->fan_alarm & BIT(index)));
}
static ssize_t
clear_fan_alarm(struct device *dev, struct device_attribute *devattr,
const char *buf, size_t count)
{
struct fts_data *data = dev_get_drvdata(dev);
int index = to_sensor_dev_attr(devattr)->index;
long ret;
ret = fts_update_device(data);
if (ret < 0)
return ret;
if (kstrtoul(buf, 10, &ret) || ret != 0)
return -EINVAL;
mutex_lock(&data->update_lock);
ret = fts_read_byte(data->client, FTS_REG_FAN_CONTROL(index));
if (ret < 0)
goto error;
ret = fts_write_byte(data->client, FTS_REG_FAN_CONTROL(index),
ret | 0x1);
if (ret < 0)
goto error;
data->valid = false;
error:
mutex_unlock(&data->update_lock);
return ret;
}
/*****************************************************************************/
/* SysFS structs */
/*****************************************************************************/
/* Temprature sensors */
static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, show_temp_value, NULL, 0);
static SENSOR_DEVICE_ATTR(temp2_input, S_IRUGO, show_temp_value, NULL, 1);
static SENSOR_DEVICE_ATTR(temp3_input, S_IRUGO, show_temp_value, NULL, 2);
static SENSOR_DEVICE_ATTR(temp4_input, S_IRUGO, show_temp_value, NULL, 3);
static SENSOR_DEVICE_ATTR(temp5_input, S_IRUGO, show_temp_value, NULL, 4);
static SENSOR_DEVICE_ATTR(temp6_input, S_IRUGO, show_temp_value, NULL, 5);
static SENSOR_DEVICE_ATTR(temp7_input, S_IRUGO, show_temp_value, NULL, 6);
static SENSOR_DEVICE_ATTR(temp8_input, S_IRUGO, show_temp_value, NULL, 7);
static SENSOR_DEVICE_ATTR(temp9_input, S_IRUGO, show_temp_value, NULL, 8);
static SENSOR_DEVICE_ATTR(temp10_input, S_IRUGO, show_temp_value, NULL, 9);
static SENSOR_DEVICE_ATTR(temp11_input, S_IRUGO, show_temp_value, NULL, 10);
static SENSOR_DEVICE_ATTR(temp12_input, S_IRUGO, show_temp_value, NULL, 11);
static SENSOR_DEVICE_ATTR(temp13_input, S_IRUGO, show_temp_value, NULL, 12);
static SENSOR_DEVICE_ATTR(temp14_input, S_IRUGO, show_temp_value, NULL, 13);
static SENSOR_DEVICE_ATTR(temp15_input, S_IRUGO, show_temp_value, NULL, 14);
static SENSOR_DEVICE_ATTR(temp16_input, S_IRUGO, show_temp_value, NULL, 15);
static SENSOR_DEVICE_ATTR(temp1_fault, S_IRUGO, show_temp_fault, NULL, 0);
static SENSOR_DEVICE_ATTR(temp2_fault, S_IRUGO, show_temp_fault, NULL, 1);
static SENSOR_DEVICE_ATTR(temp3_fault, S_IRUGO, show_temp_fault, NULL, 2);
static SENSOR_DEVICE_ATTR(temp4_fault, S_IRUGO, show_temp_fault, NULL, 3);
static SENSOR_DEVICE_ATTR(temp5_fault, S_IRUGO, show_temp_fault, NULL, 4);
static SENSOR_DEVICE_ATTR(temp6_fault, S_IRUGO, show_temp_fault, NULL, 5);
static SENSOR_DEVICE_ATTR(temp7_fault, S_IRUGO, show_temp_fault, NULL, 6);
static SENSOR_DEVICE_ATTR(temp8_fault, S_IRUGO, show_temp_fault, NULL, 7);
static SENSOR_DEVICE_ATTR(temp9_fault, S_IRUGO, show_temp_fault, NULL, 8);
static SENSOR_DEVICE_ATTR(temp10_fault, S_IRUGO, show_temp_fault, NULL, 9);
static SENSOR_DEVICE_ATTR(temp11_fault, S_IRUGO, show_temp_fault, NULL, 10);
static SENSOR_DEVICE_ATTR(temp12_fault, S_IRUGO, show_temp_fault, NULL, 11);
static SENSOR_DEVICE_ATTR(temp13_fault, S_IRUGO, show_temp_fault, NULL, 12);
static SENSOR_DEVICE_ATTR(temp14_fault, S_IRUGO, show_temp_fault, NULL, 13);
static SENSOR_DEVICE_ATTR(temp15_fault, S_IRUGO, show_temp_fault, NULL, 14);
static SENSOR_DEVICE_ATTR(temp16_fault, S_IRUGO, show_temp_fault, NULL, 15);
static SENSOR_DEVICE_ATTR(temp1_alarm, S_IRUGO | S_IWUSR, show_temp_alarm,
clear_temp_alarm, 0);
static SENSOR_DEVICE_ATTR(temp2_alarm, S_IRUGO | S_IWUSR, show_temp_alarm,
clear_temp_alarm, 1);
static SENSOR_DEVICE_ATTR(temp3_alarm, S_IRUGO | S_IWUSR, show_temp_alarm,
clear_temp_alarm, 2);
static SENSOR_DEVICE_ATTR(temp4_alarm, S_IRUGO | S_IWUSR, show_temp_alarm,
clear_temp_alarm, 3);
static SENSOR_DEVICE_ATTR(temp5_alarm, S_IRUGO | S_IWUSR, show_temp_alarm,
clear_temp_alarm, 4);
static SENSOR_DEVICE_ATTR(temp6_alarm, S_IRUGO | S_IWUSR, show_temp_alarm,
clear_temp_alarm, 5);
static SENSOR_DEVICE_ATTR(temp7_alarm, S_IRUGO | S_IWUSR, show_temp_alarm,
clear_temp_alarm, 6);
static SENSOR_DEVICE_ATTR(temp8_alarm, S_IRUGO | S_IWUSR, show_temp_alarm,
clear_temp_alarm, 7);
static SENSOR_DEVICE_ATTR(temp9_alarm, S_IRUGO | S_IWUSR, show_temp_alarm,
clear_temp_alarm, 8);
static SENSOR_DEVICE_ATTR(temp10_alarm, S_IRUGO | S_IWUSR, show_temp_alarm,
clear_temp_alarm, 9);
static SENSOR_DEVICE_ATTR(temp11_alarm, S_IRUGO | S_IWUSR, show_temp_alarm,
clear_temp_alarm, 10);
static SENSOR_DEVICE_ATTR(temp12_alarm, S_IRUGO | S_IWUSR, show_temp_alarm,
clear_temp_alarm, 11);
static SENSOR_DEVICE_ATTR(temp13_alarm, S_IRUGO | S_IWUSR, show_temp_alarm,
clear_temp_alarm, 12);
static SENSOR_DEVICE_ATTR(temp14_alarm, S_IRUGO | S_IWUSR, show_temp_alarm,
clear_temp_alarm, 13);
static SENSOR_DEVICE_ATTR(temp15_alarm, S_IRUGO | S_IWUSR, show_temp_alarm,
clear_temp_alarm, 14);
static SENSOR_DEVICE_ATTR(temp16_alarm, S_IRUGO | S_IWUSR, show_temp_alarm,
clear_temp_alarm, 15);
static struct attribute *fts_temp_attrs[] = {
&sensor_dev_attr_temp1_input.dev_attr.attr,
&sensor_dev_attr_temp2_input.dev_attr.attr,
&sensor_dev_attr_temp3_input.dev_attr.attr,
&sensor_dev_attr_temp4_input.dev_attr.attr,
&sensor_dev_attr_temp5_input.dev_attr.attr,
&sensor_dev_attr_temp6_input.dev_attr.attr,
&sensor_dev_attr_temp7_input.dev_attr.attr,
&sensor_dev_attr_temp8_input.dev_attr.attr,
&sensor_dev_attr_temp9_input.dev_attr.attr,
&sensor_dev_attr_temp10_input.dev_attr.attr,
&sensor_dev_attr_temp11_input.dev_attr.attr,
&sensor_dev_attr_temp12_input.dev_attr.attr,
&sensor_dev_attr_temp13_input.dev_attr.attr,
&sensor_dev_attr_temp14_input.dev_attr.attr,
&sensor_dev_attr_temp15_input.dev_attr.attr,
&sensor_dev_attr_temp16_input.dev_attr.attr,
&sensor_dev_attr_temp1_fault.dev_attr.attr,
&sensor_dev_attr_temp2_fault.dev_attr.attr,
&sensor_dev_attr_temp3_fault.dev_attr.attr,
&sensor_dev_attr_temp4_fault.dev_attr.attr,
&sensor_dev_attr_temp5_fault.dev_attr.attr,
&sensor_dev_attr_temp6_fault.dev_attr.attr,
&sensor_dev_attr_temp7_fault.dev_attr.attr,
&sensor_dev_attr_temp8_fault.dev_attr.attr,
&sensor_dev_attr_temp9_fault.dev_attr.attr,
&sensor_dev_attr_temp10_fault.dev_attr.attr,
&sensor_dev_attr_temp11_fault.dev_attr.attr,
&sensor_dev_attr_temp12_fault.dev_attr.attr,
&sensor_dev_attr_temp13_fault.dev_attr.attr,
&sensor_dev_attr_temp14_fault.dev_attr.attr,
&sensor_dev_attr_temp15_fault.dev_attr.attr,
&sensor_dev_attr_temp16_fault.dev_attr.attr,
&sensor_dev_attr_temp1_alarm.dev_attr.attr,
&sensor_dev_attr_temp2_alarm.dev_attr.attr,
&sensor_dev_attr_temp3_alarm.dev_attr.attr,
&sensor_dev_attr_temp4_alarm.dev_attr.attr,
&sensor_dev_attr_temp5_alarm.dev_attr.attr,
&sensor_dev_attr_temp6_alarm.dev_attr.attr,
&sensor_dev_attr_temp7_alarm.dev_attr.attr,
&sensor_dev_attr_temp8_alarm.dev_attr.attr,
&sensor_dev_attr_temp9_alarm.dev_attr.attr,
&sensor_dev_attr_temp10_alarm.dev_attr.attr,
&sensor_dev_attr_temp11_alarm.dev_attr.attr,
&sensor_dev_attr_temp12_alarm.dev_attr.attr,
&sensor_dev_attr_temp13_alarm.dev_attr.attr,
&sensor_dev_attr_temp14_alarm.dev_attr.attr,
&sensor_dev_attr_temp15_alarm.dev_attr.attr,
&sensor_dev_attr_temp16_alarm.dev_attr.attr,
NULL
};
/* Fans */
static SENSOR_DEVICE_ATTR(fan1_input, S_IRUGO, show_fan_value, NULL, 0);
static SENSOR_DEVICE_ATTR(fan2_input, S_IRUGO, show_fan_value, NULL, 1);
static SENSOR_DEVICE_ATTR(fan3_input, S_IRUGO, show_fan_value, NULL, 2);
static SENSOR_DEVICE_ATTR(fan4_input, S_IRUGO, show_fan_value, NULL, 3);
static SENSOR_DEVICE_ATTR(fan5_input, S_IRUGO, show_fan_value, NULL, 4);
static SENSOR_DEVICE_ATTR(fan6_input, S_IRUGO, show_fan_value, NULL, 5);
static SENSOR_DEVICE_ATTR(fan7_input, S_IRUGO, show_fan_value, NULL, 6);
static SENSOR_DEVICE_ATTR(fan8_input, S_IRUGO, show_fan_value, NULL, 7);
static SENSOR_DEVICE_ATTR(fan1_source, S_IRUGO, show_fan_source, NULL, 0);
static SENSOR_DEVICE_ATTR(fan2_source, S_IRUGO, show_fan_source, NULL, 1);
static SENSOR_DEVICE_ATTR(fan3_source, S_IRUGO, show_fan_source, NULL, 2);
static SENSOR_DEVICE_ATTR(fan4_source, S_IRUGO, show_fan_source, NULL, 3);
static SENSOR_DEVICE_ATTR(fan5_source, S_IRUGO, show_fan_source, NULL, 4);
static SENSOR_DEVICE_ATTR(fan6_source, S_IRUGO, show_fan_source, NULL, 5);
static SENSOR_DEVICE_ATTR(fan7_source, S_IRUGO, show_fan_source, NULL, 6);
static SENSOR_DEVICE_ATTR(fan8_source, S_IRUGO, show_fan_source, NULL, 7);
static SENSOR_DEVICE_ATTR(fan1_alarm, S_IRUGO | S_IWUSR,
show_fan_alarm, clear_fan_alarm, 0);
static SENSOR_DEVICE_ATTR(fan2_alarm, S_IRUGO | S_IWUSR,
show_fan_alarm, clear_fan_alarm, 1);
static SENSOR_DEVICE_ATTR(fan3_alarm, S_IRUGO | S_IWUSR,
show_fan_alarm, clear_fan_alarm, 2);
static SENSOR_DEVICE_ATTR(fan4_alarm, S_IRUGO | S_IWUSR,
show_fan_alarm, clear_fan_alarm, 3);
static SENSOR_DEVICE_ATTR(fan5_alarm, S_IRUGO | S_IWUSR,
show_fan_alarm, clear_fan_alarm, 4);
static SENSOR_DEVICE_ATTR(fan6_alarm, S_IRUGO | S_IWUSR,
show_fan_alarm, clear_fan_alarm, 5);
static SENSOR_DEVICE_ATTR(fan7_alarm, S_IRUGO | S_IWUSR,
show_fan_alarm, clear_fan_alarm, 6);
static SENSOR_DEVICE_ATTR(fan8_alarm, S_IRUGO | S_IWUSR,
show_fan_alarm, clear_fan_alarm, 7);
static struct attribute *fts_fan_attrs[] = {
&sensor_dev_attr_fan1_input.dev_attr.attr,
&sensor_dev_attr_fan2_input.dev_attr.attr,
&sensor_dev_attr_fan3_input.dev_attr.attr,
&sensor_dev_attr_fan4_input.dev_attr.attr,
&sensor_dev_attr_fan5_input.dev_attr.attr,
&sensor_dev_attr_fan6_input.dev_attr.attr,
&sensor_dev_attr_fan7_input.dev_attr.attr,
&sensor_dev_attr_fan8_input.dev_attr.attr,
&sensor_dev_attr_fan1_source.dev_attr.attr,
&sensor_dev_attr_fan2_source.dev_attr.attr,
&sensor_dev_attr_fan3_source.dev_attr.attr,
&sensor_dev_attr_fan4_source.dev_attr.attr,
&sensor_dev_attr_fan5_source.dev_attr.attr,
&sensor_dev_attr_fan6_source.dev_attr.attr,
&sensor_dev_attr_fan7_source.dev_attr.attr,
&sensor_dev_attr_fan8_source.dev_attr.attr,
&sensor_dev_attr_fan1_alarm.dev_attr.attr,
&sensor_dev_attr_fan2_alarm.dev_attr.attr,
&sensor_dev_attr_fan3_alarm.dev_attr.attr,
&sensor_dev_attr_fan4_alarm.dev_attr.attr,
&sensor_dev_attr_fan5_alarm.dev_attr.attr,
&sensor_dev_attr_fan6_alarm.dev_attr.attr,
&sensor_dev_attr_fan7_alarm.dev_attr.attr,
&sensor_dev_attr_fan8_alarm.dev_attr.attr,
NULL
};
/* Voltages */
static SENSOR_DEVICE_ATTR(in1_input, S_IRUGO, show_in_value, NULL, 0);
static SENSOR_DEVICE_ATTR(in2_input, S_IRUGO, show_in_value, NULL, 1);
static SENSOR_DEVICE_ATTR(in3_input, S_IRUGO, show_in_value, NULL, 2);
static SENSOR_DEVICE_ATTR(in4_input, S_IRUGO, show_in_value, NULL, 3);
static struct attribute *fts_voltage_attrs[] = {
&sensor_dev_attr_in1_input.dev_attr.attr,
&sensor_dev_attr_in2_input.dev_attr.attr,
&sensor_dev_attr_in3_input.dev_attr.attr,
&sensor_dev_attr_in4_input.dev_attr.attr,
NULL
};
static const struct attribute_group fts_voltage_attr_group = {
.attrs = fts_voltage_attrs
};
static const struct attribute_group fts_temp_attr_group = {
.attrs = fts_temp_attrs
};
static const struct attribute_group fts_fan_attr_group = {
.attrs = fts_fan_attrs
};
static const struct attribute_group *fts_attr_groups[] = {
&fts_voltage_attr_group,
&fts_temp_attr_group,
&fts_fan_attr_group,
NULL
};
/*****************************************************************************/
/* Module initialization / remove functions */
/*****************************************************************************/
static int fts_remove(struct i2c_client *client)
{
struct fts_data *data = dev_get_drvdata(&client->dev);
watchdog_unregister_device(&data->wdd);
return 0;
}
static int fts_probe(struct i2c_client *client, const struct i2c_device_id *id)
{
u8 revision;
struct fts_data *data;
int err;
s8 deviceid;
struct device *hwmon_dev;
if (client->addr != 0x73)
return -ENODEV;
/* Baseboard Management Controller check */
deviceid = i2c_smbus_read_byte_data(client, FTS_DEVICE_ID_REG);
if (deviceid > 0 && (deviceid & 0xF0) == 0x10) {
switch (deviceid & 0x0F) {
case 0x01:
break;
default:
dev_dbg(&client->dev,
"No Baseboard Management Controller\n");
return -ENODEV;
}
} else {
dev_dbg(&client->dev, "No fujitsu board\n");
return -ENODEV;
}
data = devm_kzalloc(&client->dev, sizeof(struct fts_data),
GFP_KERNEL);
if (!data)
return -ENOMEM;
mutex_init(&data->update_lock);
mutex_init(&data->access_lock);
data->client = client;
dev_set_drvdata(&client->dev, data);
err = i2c_smbus_read_byte_data(client, FTS_DEVICE_REVISION_REG);
if (err < 0)
return err;
revision = err;
hwmon_dev = devm_hwmon_device_register_with_groups(&client->dev,
"ftsteutates",
data,
fts_attr_groups);
if (IS_ERR(hwmon_dev))
return PTR_ERR(hwmon_dev);
err = fts_watchdog_init(data);
if (err)
return err;
dev_info(&client->dev, "Detected FTS Teutates chip, revision: %d.%d\n",
(revision & 0xF0) >> 4, revision & 0x0F);
return 0;
}
/*****************************************************************************/
/* Module Details */
/*****************************************************************************/
static struct i2c_driver fts_driver = {
.driver = {
.name = "ftsteutates",
},
.id_table = fts_id,
.probe = fts_probe,
.remove = fts_remove,
};
module_i2c_driver(fts_driver);
MODULE_AUTHOR("Thilo Cestonaro <thilo.cestonaro@ts.fujitsu.com>");
MODULE_DESCRIPTION("FTS Teutates driver");
MODULE_LICENSE("GPL");

445
drivers/hwmon/ina3221.c Normal file
View File

@ -0,0 +1,445 @@
/*
* INA3221 Triple Current/Voltage Monitor
*
* Copyright (C) 2016 Texas Instruments Incorporated - http://www.ti.com/
* Andrew F. Davis <afd@ti.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*/
#include <linux/hwmon.h>
#include <linux/hwmon-sysfs.h>
#include <linux/i2c.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/regmap.h>
#define INA3221_DRIVER_NAME "ina3221"
#define INA3221_CONFIG 0x00
#define INA3221_SHUNT1 0x01
#define INA3221_BUS1 0x02
#define INA3221_SHUNT2 0x03
#define INA3221_BUS2 0x04
#define INA3221_SHUNT3 0x05
#define INA3221_BUS3 0x06
#define INA3221_CRIT1 0x07
#define INA3221_WARN1 0x08
#define INA3221_CRIT2 0x09
#define INA3221_WARN2 0x0a
#define INA3221_CRIT3 0x0b
#define INA3221_WARN3 0x0c
#define INA3221_MASK_ENABLE 0x0f
#define INA3221_CONFIG_MODE_SHUNT BIT(1)
#define INA3221_CONFIG_MODE_BUS BIT(2)
#define INA3221_CONFIG_MODE_CONTINUOUS BIT(3)
#define INA3221_RSHUNT_DEFAULT 10000
enum ina3221_fields {
/* Configuration */
F_RST,
/* Alert Flags */
F_WF3, F_WF2, F_WF1,
F_CF3, F_CF2, F_CF1,
/* sentinel */
F_MAX_FIELDS
};
static const struct reg_field ina3221_reg_fields[] = {
[F_RST] = REG_FIELD(INA3221_CONFIG, 15, 15),
[F_WF3] = REG_FIELD(INA3221_MASK_ENABLE, 3, 3),
[F_WF2] = REG_FIELD(INA3221_MASK_ENABLE, 4, 4),
[F_WF1] = REG_FIELD(INA3221_MASK_ENABLE, 5, 5),
[F_CF3] = REG_FIELD(INA3221_MASK_ENABLE, 7, 7),
[F_CF2] = REG_FIELD(INA3221_MASK_ENABLE, 8, 8),
[F_CF1] = REG_FIELD(INA3221_MASK_ENABLE, 9, 9),
};
enum ina3221_channels {
INA3221_CHANNEL1,
INA3221_CHANNEL2,
INA3221_CHANNEL3,
INA3221_NUM_CHANNELS
};
static const unsigned int register_channel[] = {
[INA3221_SHUNT1] = INA3221_CHANNEL1,
[INA3221_SHUNT2] = INA3221_CHANNEL2,
[INA3221_SHUNT3] = INA3221_CHANNEL3,
[INA3221_CRIT1] = INA3221_CHANNEL1,
[INA3221_CRIT2] = INA3221_CHANNEL2,
[INA3221_CRIT3] = INA3221_CHANNEL3,
[INA3221_WARN1] = INA3221_CHANNEL1,
[INA3221_WARN2] = INA3221_CHANNEL2,
[INA3221_WARN3] = INA3221_CHANNEL3,
};
/**
* struct ina3221_data - device specific information
* @regmap: Register map of the device
* @fields: Register fields of the device
* @shunt_resistors: Array of resistor values per channel
*/
struct ina3221_data {
struct regmap *regmap;
struct regmap_field *fields[F_MAX_FIELDS];
int shunt_resistors[INA3221_NUM_CHANNELS];
};
static int ina3221_read_value(struct ina3221_data *ina, unsigned int reg,
int *val)
{
unsigned int regval;
int ret;
ret = regmap_read(ina->regmap, reg, &regval);
if (ret)
return ret;
*val = sign_extend32(regval >> 3, 12);
return 0;
}
static ssize_t ina3221_show_bus_voltage(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct sensor_device_attribute *sd_attr = to_sensor_dev_attr(attr);
struct ina3221_data *ina = dev_get_drvdata(dev);
unsigned int reg = sd_attr->index;
int val, voltage_mv, ret;
ret = ina3221_read_value(ina, reg, &val);
if (ret)
return ret;
voltage_mv = val * 8;
return snprintf(buf, PAGE_SIZE, "%d\n", voltage_mv);
}
static ssize_t ina3221_show_shunt_voltage(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct sensor_device_attribute *sd_attr = to_sensor_dev_attr(attr);
struct ina3221_data *ina = dev_get_drvdata(dev);
unsigned int reg = sd_attr->index;
int val, voltage_uv, ret;
ret = ina3221_read_value(ina, reg, &val);
if (ret)
return ret;
voltage_uv = val * 40;
return snprintf(buf, PAGE_SIZE, "%d\n", voltage_uv);
}
static ssize_t ina3221_show_current(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct sensor_device_attribute *sd_attr = to_sensor_dev_attr(attr);
struct ina3221_data *ina = dev_get_drvdata(dev);
unsigned int reg = sd_attr->index;
unsigned int channel = register_channel[reg];
int resistance_uo = ina->shunt_resistors[channel];
int val, current_ma, voltage_nv, ret;
ret = ina3221_read_value(ina, reg, &val);
if (ret)
return ret;
voltage_nv = val * 40000;
current_ma = DIV_ROUND_CLOSEST(voltage_nv, resistance_uo);
return snprintf(buf, PAGE_SIZE, "%d\n", current_ma);
}
static ssize_t ina3221_set_current(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
struct sensor_device_attribute *sd_attr = to_sensor_dev_attr(attr);
struct ina3221_data *ina = dev_get_drvdata(dev);
unsigned int reg = sd_attr->index;
unsigned int channel = register_channel[reg];
int resistance_uo = ina->shunt_resistors[channel];
int val, current_ma, voltage_uv, ret;
ret = kstrtoint(buf, 0, &current_ma);
if (ret)
return ret;
/* clamp current */
current_ma = clamp_val(current_ma,
INT_MIN / resistance_uo,
INT_MAX / resistance_uo);
voltage_uv = DIV_ROUND_CLOSEST(current_ma * resistance_uo, 1000);
/* clamp voltage */
voltage_uv = clamp_val(voltage_uv, -163800, 163800);
/* 1 / 40uV(scale) << 3(register shift) = 5 */
val = DIV_ROUND_CLOSEST(voltage_uv, 5) & 0xfff8;
ret = regmap_write(ina->regmap, reg, val);
if (ret)
return ret;
return count;
}
static ssize_t ina3221_show_shunt(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct sensor_device_attribute *sd_attr = to_sensor_dev_attr(attr);
struct ina3221_data *ina = dev_get_drvdata(dev);
unsigned int channel = sd_attr->index;
unsigned int resistance_uo;
resistance_uo = ina->shunt_resistors[channel];
return snprintf(buf, PAGE_SIZE, "%d\n", resistance_uo);
}
static ssize_t ina3221_set_shunt(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
struct sensor_device_attribute *sd_attr = to_sensor_dev_attr(attr);
struct ina3221_data *ina = dev_get_drvdata(dev);
unsigned int channel = sd_attr->index;
int val;
int ret;
ret = kstrtoint(buf, 0, &val);
if (ret)
return ret;
val = clamp_val(val, 1, INT_MAX);
ina->shunt_resistors[channel] = val;
return count;
}
static ssize_t ina3221_show_alert(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct sensor_device_attribute *sd_attr = to_sensor_dev_attr(attr);
struct ina3221_data *ina = dev_get_drvdata(dev);
unsigned int field = sd_attr->index;
unsigned int regval;
int ret;
ret = regmap_field_read(ina->fields[field], &regval);
if (ret)
return ret;
return snprintf(buf, PAGE_SIZE, "%d\n", regval);
}
/* bus voltage */
static SENSOR_DEVICE_ATTR(in1_input, S_IRUGO,
ina3221_show_bus_voltage, NULL, INA3221_BUS1);
static SENSOR_DEVICE_ATTR(in2_input, S_IRUGO,
ina3221_show_bus_voltage, NULL, INA3221_BUS2);
static SENSOR_DEVICE_ATTR(in3_input, S_IRUGO,
ina3221_show_bus_voltage, NULL, INA3221_BUS3);
/* calculated current */
static SENSOR_DEVICE_ATTR(curr1_input, S_IRUGO,
ina3221_show_current, NULL, INA3221_SHUNT1);
static SENSOR_DEVICE_ATTR(curr2_input, S_IRUGO,
ina3221_show_current, NULL, INA3221_SHUNT2);
static SENSOR_DEVICE_ATTR(curr3_input, S_IRUGO,
ina3221_show_current, NULL, INA3221_SHUNT3);
/* shunt resistance */
static SENSOR_DEVICE_ATTR(shunt1_resistor, S_IRUGO | S_IWUSR,
ina3221_show_shunt, ina3221_set_shunt, INA3221_CHANNEL1);
static SENSOR_DEVICE_ATTR(shunt2_resistor, S_IRUGO | S_IWUSR,
ina3221_show_shunt, ina3221_set_shunt, INA3221_CHANNEL2);
static SENSOR_DEVICE_ATTR(shunt3_resistor, S_IRUGO | S_IWUSR,
ina3221_show_shunt, ina3221_set_shunt, INA3221_CHANNEL3);
/* critical current */
static SENSOR_DEVICE_ATTR(curr1_crit, S_IRUGO | S_IWUSR,
ina3221_show_current, ina3221_set_current, INA3221_CRIT1);
static SENSOR_DEVICE_ATTR(curr2_crit, S_IRUGO | S_IWUSR,
ina3221_show_current, ina3221_set_current, INA3221_CRIT2);
static SENSOR_DEVICE_ATTR(curr3_crit, S_IRUGO | S_IWUSR,
ina3221_show_current, ina3221_set_current, INA3221_CRIT3);
/* critical current alert */
static SENSOR_DEVICE_ATTR(curr1_crit_alarm, S_IRUGO,
ina3221_show_alert, NULL, F_CF1);
static SENSOR_DEVICE_ATTR(curr2_crit_alarm, S_IRUGO,
ina3221_show_alert, NULL, F_CF2);
static SENSOR_DEVICE_ATTR(curr3_crit_alarm, S_IRUGO,
ina3221_show_alert, NULL, F_CF3);
/* warning current */
static SENSOR_DEVICE_ATTR(curr1_max, S_IRUGO | S_IWUSR,
ina3221_show_current, ina3221_set_current, INA3221_WARN1);
static SENSOR_DEVICE_ATTR(curr2_max, S_IRUGO | S_IWUSR,
ina3221_show_current, ina3221_set_current, INA3221_WARN2);
static SENSOR_DEVICE_ATTR(curr3_max, S_IRUGO | S_IWUSR,
ina3221_show_current, ina3221_set_current, INA3221_WARN3);
/* warning current alert */
static SENSOR_DEVICE_ATTR(curr1_max_alarm, S_IRUGO,
ina3221_show_alert, NULL, F_WF1);
static SENSOR_DEVICE_ATTR(curr2_max_alarm, S_IRUGO,
ina3221_show_alert, NULL, F_WF2);
static SENSOR_DEVICE_ATTR(curr3_max_alarm, S_IRUGO,
ina3221_show_alert, NULL, F_WF3);
/* shunt voltage */
static SENSOR_DEVICE_ATTR(in4_input, S_IRUGO,
ina3221_show_shunt_voltage, NULL, INA3221_SHUNT1);
static SENSOR_DEVICE_ATTR(in5_input, S_IRUGO,
ina3221_show_shunt_voltage, NULL, INA3221_SHUNT2);
static SENSOR_DEVICE_ATTR(in6_input, S_IRUGO,
ina3221_show_shunt_voltage, NULL, INA3221_SHUNT3);
static struct attribute *ina3221_attrs[] = {
/* channel 1 */
&sensor_dev_attr_in1_input.dev_attr.attr,
&sensor_dev_attr_curr1_input.dev_attr.attr,
&sensor_dev_attr_shunt1_resistor.dev_attr.attr,
&sensor_dev_attr_curr1_crit.dev_attr.attr,
&sensor_dev_attr_curr1_crit_alarm.dev_attr.attr,
&sensor_dev_attr_curr1_max.dev_attr.attr,
&sensor_dev_attr_curr1_max_alarm.dev_attr.attr,
&sensor_dev_attr_in4_input.dev_attr.attr,
/* channel 2 */
&sensor_dev_attr_in2_input.dev_attr.attr,
&sensor_dev_attr_curr2_input.dev_attr.attr,
&sensor_dev_attr_shunt2_resistor.dev_attr.attr,
&sensor_dev_attr_curr2_crit.dev_attr.attr,
&sensor_dev_attr_curr2_crit_alarm.dev_attr.attr,
&sensor_dev_attr_curr2_max.dev_attr.attr,
&sensor_dev_attr_curr2_max_alarm.dev_attr.attr,
&sensor_dev_attr_in5_input.dev_attr.attr,
/* channel 3 */
&sensor_dev_attr_in3_input.dev_attr.attr,
&sensor_dev_attr_curr3_input.dev_attr.attr,
&sensor_dev_attr_shunt3_resistor.dev_attr.attr,
&sensor_dev_attr_curr3_crit.dev_attr.attr,
&sensor_dev_attr_curr3_crit_alarm.dev_attr.attr,
&sensor_dev_attr_curr3_max.dev_attr.attr,
&sensor_dev_attr_curr3_max_alarm.dev_attr.attr,
&sensor_dev_attr_in6_input.dev_attr.attr,
NULL,
};
ATTRIBUTE_GROUPS(ina3221);
static const struct regmap_range ina3221_yes_ranges[] = {
regmap_reg_range(INA3221_SHUNT1, INA3221_BUS3),
regmap_reg_range(INA3221_MASK_ENABLE, INA3221_MASK_ENABLE),
};
static const struct regmap_access_table ina3221_volatile_table = {
.yes_ranges = ina3221_yes_ranges,
.n_yes_ranges = ARRAY_SIZE(ina3221_yes_ranges),
};
static const struct regmap_config ina3221_regmap_config = {
.reg_bits = 8,
.val_bits = 16,
.cache_type = REGCACHE_RBTREE,
.volatile_table = &ina3221_volatile_table,
};
static int ina3221_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct device *dev = &client->dev;
struct ina3221_data *ina;
struct device *hwmon_dev;
int i, ret;
ina = devm_kzalloc(dev, sizeof(*ina), GFP_KERNEL);
if (!ina)
return -ENOMEM;
ina->regmap = devm_regmap_init_i2c(client, &ina3221_regmap_config);
if (IS_ERR(ina->regmap)) {
dev_err(dev, "Unable to allocate register map\n");
return PTR_ERR(ina->regmap);
}
for (i = 0; i < F_MAX_FIELDS; i++) {
ina->fields[i] = devm_regmap_field_alloc(dev,
ina->regmap,
ina3221_reg_fields[i]);
if (IS_ERR(ina->fields[i])) {
dev_err(dev, "Unable to allocate regmap fields\n");
return PTR_ERR(ina->fields[i]);
}
}
for (i = 0; i < INA3221_NUM_CHANNELS; i++)
ina->shunt_resistors[i] = INA3221_RSHUNT_DEFAULT;
ret = regmap_field_write(ina->fields[F_RST], true);
if (ret) {
dev_err(dev, "Unable to reset device\n");
return ret;
}
hwmon_dev = devm_hwmon_device_register_with_groups(dev,
client->name,
ina, ina3221_groups);
if (IS_ERR(hwmon_dev)) {
dev_err(dev, "Unable to register hwmon device\n");
return PTR_ERR(hwmon_dev);
}
return 0;
}
static const struct of_device_id ina3221_of_match_table[] = {
{ .compatible = "ti,ina3221", },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, ina3221_of_match_table);
static const struct i2c_device_id ina3221_ids[] = {
{ "ina3221", 0 },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(i2c, ina3221_ids);
static struct i2c_driver ina3221_i2c_driver = {
.probe = ina3221_probe,
.driver = {
.name = INA3221_DRIVER_NAME,
.of_match_table = ina3221_of_match_table,
},
.id_table = ina3221_ids,
};
module_i2c_driver(ina3221_i2c_driver);
MODULE_AUTHOR("Andrew F. Davis <afd@ti.com>");
MODULE_DESCRIPTION("Texas Instruments INA3221 HWMon Driver");
MODULE_LICENSE("GPL v2");

View File

@ -31,6 +31,7 @@
#include <linux/hwmon-sysfs.h>
#include <linux/err.h>
#include <linux/mutex.h>
#include <linux/of.h>
/* Addresses to scan */
static const unsigned short normal_i2c[] = {
@ -104,6 +105,9 @@ static const unsigned short normal_i2c[] = {
#define MCP9804_DEVID 0x0200
#define MCP9804_DEVID_MASK 0xfffc
#define MCP9808_DEVID 0x0400
#define MCP9808_DEVID_MASK 0xfffc
#define MCP98242_DEVID 0x2000
#define MCP98242_DEVID_MASK 0xfffc
@ -160,6 +164,7 @@ static struct jc42_chips jc42_chips[] = {
{ IDT_MANID, TS3001_DEVID, TS3001_DEVID_MASK },
{ MAX_MANID, MAX6604_DEVID, MAX6604_DEVID_MASK },
{ MCP_MANID, MCP9804_DEVID, MCP9804_DEVID_MASK },
{ MCP_MANID, MCP9808_DEVID, MCP9808_DEVID_MASK },
{ MCP_MANID, MCP98242_DEVID, MCP98242_DEVID_MASK },
{ MCP_MANID, MCP98243_DEVID, MCP98243_DEVID_MASK },
{ MCP_MANID, MCP98244_DEVID, MCP98244_DEVID_MASK },
@ -537,11 +542,20 @@ static const struct i2c_device_id jc42_id[] = {
};
MODULE_DEVICE_TABLE(i2c, jc42_id);
#ifdef CONFIG_OF
static const struct of_device_id jc42_of_ids[] = {
{ .compatible = "jedec,jc-42.4-temp", },
{ }
};
MODULE_DEVICE_TABLE(of, jc42_of_ids);
#endif
static struct i2c_driver jc42_driver = {
.class = I2C_CLASS_SPD,
.class = I2C_CLASS_SPD | I2C_CLASS_HWMON,
.driver = {
.name = "jc42",
.pm = JC42_DEV_PM_OPS,
.of_match_table = of_match_ptr(jc42_of_ids),
},
.probe = jc42_probe,
.remove = jc42_remove,

View File

@ -29,23 +29,13 @@
struct jz4740_hwmon {
void __iomem *base;
int irq;
const struct mfd_cell *cell;
struct device *hwmon;
struct platform_device *pdev;
struct completion read_completion;
struct mutex lock;
};
static ssize_t jz4740_hwmon_show_name(struct device *dev,
struct device_attribute *dev_attr, char *buf)
{
return sprintf(buf, "jz4740\n");
}
static irqreturn_t jz4740_hwmon_irq(int irq, void *data)
{
struct jz4740_hwmon *hwmon = data;
@ -58,6 +48,7 @@ static ssize_t jz4740_hwmon_read_adcin(struct device *dev,
struct device_attribute *dev_attr, char *buf)
{
struct jz4740_hwmon *hwmon = dev_get_drvdata(dev);
struct platform_device *pdev = hwmon->pdev;
struct completion *completion = &hwmon->read_completion;
long t;
unsigned long val;
@ -68,7 +59,7 @@ static ssize_t jz4740_hwmon_read_adcin(struct device *dev,
reinit_completion(completion);
enable_irq(hwmon->irq);
hwmon->cell->enable(to_platform_device(dev));
hwmon->cell->enable(pdev);
t = wait_for_completion_interruptible_timeout(completion, HZ);
@ -80,7 +71,7 @@ static ssize_t jz4740_hwmon_read_adcin(struct device *dev,
ret = t ? t : -ETIMEDOUT;
}
hwmon->cell->disable(to_platform_device(dev));
hwmon->cell->disable(pdev);
disable_irq(hwmon->irq);
mutex_unlock(&hwmon->lock);
@ -88,26 +79,24 @@ static ssize_t jz4740_hwmon_read_adcin(struct device *dev,
return ret;
}
static DEVICE_ATTR(name, S_IRUGO, jz4740_hwmon_show_name, NULL);
static DEVICE_ATTR(in0_input, S_IRUGO, jz4740_hwmon_read_adcin, NULL);
static struct attribute *jz4740_hwmon_attributes[] = {
&dev_attr_name.attr,
static struct attribute *jz4740_attrs[] = {
&dev_attr_in0_input.attr,
NULL
};
static const struct attribute_group jz4740_hwmon_attr_group = {
.attrs = jz4740_hwmon_attributes,
};
ATTRIBUTE_GROUPS(jz4740);
static int jz4740_hwmon_probe(struct platform_device *pdev)
{
int ret;
struct device *dev = &pdev->dev;
struct jz4740_hwmon *hwmon;
struct device *hwmon_dev;
struct resource *mem;
hwmon = devm_kzalloc(&pdev->dev, sizeof(*hwmon), GFP_KERNEL);
hwmon = devm_kzalloc(dev, sizeof(*hwmon), GFP_KERNEL);
if (!hwmon)
return -ENOMEM;
@ -125,12 +114,11 @@ static int jz4740_hwmon_probe(struct platform_device *pdev)
if (IS_ERR(hwmon->base))
return PTR_ERR(hwmon->base);
hwmon->pdev = pdev;
init_completion(&hwmon->read_completion);
mutex_init(&hwmon->lock);
platform_set_drvdata(pdev, hwmon);
ret = devm_request_irq(&pdev->dev, hwmon->irq, jz4740_hwmon_irq, 0,
ret = devm_request_irq(dev, hwmon->irq, jz4740_hwmon_irq, 0,
pdev->name, hwmon);
if (ret) {
dev_err(&pdev->dev, "Failed to request irq: %d\n", ret);
@ -138,38 +126,13 @@ static int jz4740_hwmon_probe(struct platform_device *pdev)
}
disable_irq(hwmon->irq);
ret = sysfs_create_group(&pdev->dev.kobj, &jz4740_hwmon_attr_group);
if (ret) {
dev_err(&pdev->dev, "Failed to create sysfs group: %d\n", ret);
return ret;
}
hwmon->hwmon = hwmon_device_register(&pdev->dev);
if (IS_ERR(hwmon->hwmon)) {
ret = PTR_ERR(hwmon->hwmon);
goto err_remove_file;
}
return 0;
err_remove_file:
sysfs_remove_group(&pdev->dev.kobj, &jz4740_hwmon_attr_group);
return ret;
}
static int jz4740_hwmon_remove(struct platform_device *pdev)
{
struct jz4740_hwmon *hwmon = platform_get_drvdata(pdev);
hwmon_device_unregister(hwmon->hwmon);
sysfs_remove_group(&pdev->dev.kobj, &jz4740_hwmon_attr_group);
return 0;
hwmon_dev = devm_hwmon_device_register_with_groups(dev, "jz4740", hwmon,
jz4740_groups);
return PTR_ERR_OR_ZERO(hwmon_dev);
}
static struct platform_driver jz4740_hwmon_driver = {
.probe = jz4740_hwmon_probe,
.remove = jz4740_hwmon_remove,
.driver = {
.name = "jz4740-hwmon",
},

View File

@ -26,8 +26,8 @@
#include <linux/hwmon.h>
#include <linux/hwmon-sysfs.h>
#include <linux/err.h>
#include <linux/mutex.h>
#include <linux/of.h>
#include <linux/regmap.h>
#include <linux/thermal.h>
#include "lm75.h"
@ -66,35 +66,21 @@ static const unsigned short normal_i2c[] = { 0x48, 0x49, 0x4a, 0x4b, 0x4c,
/* The LM75 registers */
#define LM75_REG_TEMP 0x00
#define LM75_REG_CONF 0x01
static const u8 LM75_REG_TEMP[3] = {
0x00, /* input */
0x03, /* max */
0x02, /* hyst */
};
#define LM75_REG_HYST 0x02
#define LM75_REG_MAX 0x03
/* Each client has this additional data */
struct lm75_data {
struct i2c_client *client;
struct device *hwmon_dev;
struct mutex update_lock;
struct regmap *regmap;
u8 orig_conf;
u8 resolution; /* In bits, between 9 and 12 */
u8 resolution_limits;
char valid; /* !=0 if registers are valid */
unsigned long last_updated; /* In jiffies */
unsigned long sample_time; /* In jiffies */
s16 temp[3]; /* Register values,
0 = input
1 = max
2 = hyst */
unsigned int sample_time; /* In ms */
};
static int lm75_read_value(struct i2c_client *client, u8 reg);
static int lm75_write_value(struct i2c_client *client, u8 reg, u16 value);
static struct lm75_data *lm75_update_device(struct device *dev);
/*-----------------------------------------------------------------------*/
static inline long lm75_reg_to_mc(s16 temp, u8 resolution)
@ -106,12 +92,15 @@ static inline long lm75_reg_to_mc(s16 temp, u8 resolution)
static int lm75_read_temp(void *dev, int *temp)
{
struct lm75_data *data = lm75_update_device(dev);
struct lm75_data *data = dev_get_drvdata(dev);
unsigned int _temp;
int err;
if (IS_ERR(data))
return PTR_ERR(data);
err = regmap_read(data->regmap, LM75_REG_TEMP, &_temp);
if (err < 0)
return err;
*temp = lm75_reg_to_mc(data->temp[0], data->resolution);
*temp = lm75_reg_to_mc(_temp, data->resolution);
return 0;
}
@ -120,13 +109,15 @@ static ssize_t show_temp(struct device *dev, struct device_attribute *da,
char *buf)
{
struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
struct lm75_data *data = lm75_update_device(dev);
struct lm75_data *data = dev_get_drvdata(dev);
unsigned int temp = 0;
int err;
if (IS_ERR(data))
return PTR_ERR(data);
err = regmap_read(data->regmap, attr->index, &temp);
if (err < 0)
return err;
return sprintf(buf, "%ld\n", lm75_reg_to_mc(data->temp[attr->index],
data->resolution));
return sprintf(buf, "%ld\n", lm75_reg_to_mc(temp, data->resolution));
}
static ssize_t set_temp(struct device *dev, struct device_attribute *da,
@ -134,8 +125,6 @@ static ssize_t set_temp(struct device *dev, struct device_attribute *da,
{
struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
struct lm75_data *data = dev_get_drvdata(dev);
struct i2c_client *client = data->client;
int nr = attr->index;
long temp;
int error;
u8 resolution;
@ -153,25 +142,36 @@ static ssize_t set_temp(struct device *dev, struct device_attribute *da,
else
resolution = data->resolution;
mutex_lock(&data->update_lock);
temp = clamp_val(temp, LM75_TEMP_MIN, LM75_TEMP_MAX);
data->temp[nr] = DIV_ROUND_CLOSEST(temp << (resolution - 8),
1000) << (16 - resolution);
lm75_write_value(client, LM75_REG_TEMP[nr], data->temp[nr]);
mutex_unlock(&data->update_lock);
temp = DIV_ROUND_CLOSEST(temp << (resolution - 8),
1000) << (16 - resolution);
error = regmap_write(data->regmap, attr->index, temp);
if (error < 0)
return error;
return count;
}
static ssize_t show_update_interval(struct device *dev,
struct device_attribute *da, char *buf)
{
struct lm75_data *data = dev_get_drvdata(dev);
return sprintf(buf, "%u\n", data->sample_time);
}
static SENSOR_DEVICE_ATTR(temp1_max, S_IWUSR | S_IRUGO,
show_temp, set_temp, 1);
show_temp, set_temp, LM75_REG_MAX);
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);
show_temp, set_temp, LM75_REG_HYST);
static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, show_temp, NULL, LM75_REG_TEMP);
static DEVICE_ATTR(update_interval, S_IRUGO, show_update_interval, NULL);
static struct attribute *lm75_attrs[] = {
&sensor_dev_attr_temp1_input.dev_attr.attr,
&sensor_dev_attr_temp1_max.dev_attr.attr,
&sensor_dev_attr_temp1_max_hyst.dev_attr.attr,
&dev_attr_update_interval.attr,
NULL
};
@ -185,10 +185,40 @@ static const struct thermal_zone_of_device_ops lm75_of_thermal_ops = {
/* device probe and removal */
static bool lm75_is_writeable_reg(struct device *dev, unsigned int reg)
{
return reg != LM75_REG_TEMP;
}
static bool lm75_is_volatile_reg(struct device *dev, unsigned int reg)
{
return reg == LM75_REG_TEMP;
}
static const struct regmap_config lm75_regmap_config = {
.reg_bits = 8,
.val_bits = 16,
.max_register = LM75_REG_MAX,
.writeable_reg = lm75_is_writeable_reg,
.volatile_reg = lm75_is_volatile_reg,
.val_format_endian = REGMAP_ENDIAN_BIG,
.cache_type = REGCACHE_RBTREE,
.use_single_rw = true,
};
static void lm75_remove(void *data)
{
struct lm75_data *lm75 = data;
struct i2c_client *client = lm75->client;
i2c_smbus_write_byte_data(client, LM75_REG_CONF, lm75->orig_conf);
}
static int
lm75_probe(struct i2c_client *client, const struct i2c_device_id *id)
{
struct device *dev = &client->dev;
struct device *hwmon_dev;
struct lm75_data *data;
int status;
u8 set_mask, clr_mask;
@ -204,8 +234,10 @@ lm75_probe(struct i2c_client *client, const struct i2c_device_id *id)
return -ENOMEM;
data->client = client;
i2c_set_clientdata(client, data);
mutex_init(&data->update_lock);
data->regmap = devm_regmap_init_i2c(client, &lm75_regmap_config);
if (IS_ERR(data->regmap))
return PTR_ERR(data->regmap);
/* Set to LM75 resolution (9 bits, 1/2 degree C) and range.
* Then tweak to be more precise when appropriate.
@ -217,7 +249,7 @@ lm75_probe(struct i2c_client *client, const struct i2c_device_id *id)
case adt75:
clr_mask |= 1 << 5; /* not one-shot mode */
data->resolution = 12;
data->sample_time = HZ / 8;
data->sample_time = MSEC_PER_SEC / 8;
break;
case ds1775:
case ds75:
@ -225,35 +257,35 @@ lm75_probe(struct i2c_client *client, const struct i2c_device_id *id)
clr_mask |= 3 << 5;
set_mask |= 2 << 5; /* 11-bit mode */
data->resolution = 11;
data->sample_time = HZ;
data->sample_time = MSEC_PER_SEC;
break;
case ds7505:
set_mask |= 3 << 5; /* 12-bit mode */
data->resolution = 12;
data->sample_time = HZ / 4;
data->sample_time = MSEC_PER_SEC / 4;
break;
case g751:
case lm75:
case lm75a:
data->resolution = 9;
data->sample_time = HZ / 2;
data->sample_time = MSEC_PER_SEC / 2;
break;
case lm75b:
data->resolution = 11;
data->sample_time = HZ / 4;
data->sample_time = MSEC_PER_SEC / 4;
break;
case max6625:
data->resolution = 9;
data->sample_time = HZ / 4;
data->sample_time = MSEC_PER_SEC / 4;
break;
case max6626:
data->resolution = 12;
data->resolution_limits = 9;
data->sample_time = HZ / 4;
data->sample_time = MSEC_PER_SEC / 4;
break;
case tcn75:
data->resolution = 9;
data->sample_time = HZ / 8;
data->sample_time = MSEC_PER_SEC / 8;
break;
case mcp980x:
data->resolution_limits = 9;
@ -262,14 +294,14 @@ lm75_probe(struct i2c_client *client, const struct i2c_device_id *id)
case tmp101:
set_mask |= 3 << 5; /* 12-bit mode */
data->resolution = 12;
data->sample_time = HZ;
data->sample_time = MSEC_PER_SEC;
clr_mask |= 1 << 7; /* not one-shot mode */
break;
case tmp112:
set_mask |= 3 << 5; /* 12-bit mode */
clr_mask |= 1 << 7; /* not one-shot mode */
data->resolution = 12;
data->sample_time = HZ / 4;
data->sample_time = MSEC_PER_SEC / 4;
break;
case tmp105:
case tmp175:
@ -278,17 +310,17 @@ lm75_probe(struct i2c_client *client, const struct i2c_device_id *id)
set_mask |= 3 << 5; /* 12-bit mode */
clr_mask |= 1 << 7; /* not one-shot mode */
data->resolution = 12;
data->sample_time = HZ / 2;
data->sample_time = MSEC_PER_SEC / 2;
break;
case tmp75c:
clr_mask |= 1 << 5; /* not one-shot mode */
data->resolution = 12;
data->sample_time = HZ / 4;
data->sample_time = MSEC_PER_SEC / 4;
break;
}
/* configure as specified */
status = lm75_read_value(client, LM75_REG_CONF);
status = i2c_smbus_read_byte_data(client, LM75_REG_CONF);
if (status < 0) {
dev_dbg(dev, "Can't read config? %d\n", status);
return status;
@ -297,33 +329,26 @@ lm75_probe(struct i2c_client *client, const struct i2c_device_id *id)
new = status & ~clr_mask;
new |= set_mask;
if (status != new)
lm75_write_value(client, LM75_REG_CONF, new);
i2c_smbus_write_byte_data(client, LM75_REG_CONF, new);
devm_add_action(dev, lm75_remove, data);
dev_dbg(dev, "Config %02x\n", new);
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);
hwmon_dev = devm_hwmon_device_register_with_groups(dev, client->name,
data, lm75_groups);
if (IS_ERR(hwmon_dev))
return PTR_ERR(hwmon_dev);
devm_thermal_zone_of_sensor_register(data->hwmon_dev, 0,
data->hwmon_dev,
devm_thermal_zone_of_sensor_register(hwmon_dev, 0,
hwmon_dev,
&lm75_of_thermal_ops);
dev_info(dev, "%s: sensor '%s'\n",
dev_name(data->hwmon_dev), client->name);
dev_info(dev, "%s: sensor '%s'\n", dev_name(hwmon_dev), client->name);
return 0;
}
static int lm75_remove(struct i2c_client *client)
{
struct lm75_data *data = i2c_get_clientdata(client);
hwmon_device_unregister(data->hwmon_dev);
lm75_write_value(client, LM75_REG_CONF, data->orig_conf);
return 0;
}
static const struct i2c_device_id lm75_ids[] = {
{ "adt75", adt75, },
{ "ds1775", ds1775, },
@ -449,13 +474,13 @@ static int lm75_suspend(struct device *dev)
{
int status;
struct i2c_client *client = to_i2c_client(dev);
status = lm75_read_value(client, LM75_REG_CONF);
status = i2c_smbus_read_byte_data(client, LM75_REG_CONF);
if (status < 0) {
dev_dbg(&client->dev, "Can't read config? %d\n", status);
return status;
}
status = status | LM75_SHUTDOWN;
lm75_write_value(client, LM75_REG_CONF, status);
i2c_smbus_write_byte_data(client, LM75_REG_CONF, status);
return 0;
}
@ -463,13 +488,13 @@ static int lm75_resume(struct device *dev)
{
int status;
struct i2c_client *client = to_i2c_client(dev);
status = lm75_read_value(client, LM75_REG_CONF);
status = i2c_smbus_read_byte_data(client, LM75_REG_CONF);
if (status < 0) {
dev_dbg(&client->dev, "Can't read config? %d\n", status);
return status;
}
status = status & ~LM75_SHUTDOWN;
lm75_write_value(client, LM75_REG_CONF, status);
i2c_smbus_write_byte_data(client, LM75_REG_CONF, status);
return 0;
}
@ -489,73 +514,11 @@ static struct i2c_driver lm75_driver = {
.pm = LM75_DEV_PM_OPS,
},
.probe = lm75_probe,
.remove = lm75_remove,
.id_table = lm75_ids,
.detect = lm75_detect,
.address_list = normal_i2c,
};
/*-----------------------------------------------------------------------*/
/* register access */
/*
* All registers are word-sized, except for the configuration register.
* LM75 uses a high-byte first convention, which is exactly opposite to
* the SMBus standard.
*/
static int lm75_read_value(struct i2c_client *client, u8 reg)
{
if (reg == LM75_REG_CONF)
return i2c_smbus_read_byte_data(client, reg);
else
return i2c_smbus_read_word_swapped(client, reg);
}
static int lm75_write_value(struct i2c_client *client, u8 reg, u16 value)
{
if (reg == LM75_REG_CONF)
return i2c_smbus_write_byte_data(client, reg, value);
else
return i2c_smbus_write_word_swapped(client, reg, value);
}
static struct lm75_data *lm75_update_device(struct device *dev)
{
struct lm75_data *data = dev_get_drvdata(dev);
struct i2c_client *client = data->client;
struct lm75_data *ret = data;
mutex_lock(&data->update_lock);
if (time_after(jiffies, data->last_updated + data->sample_time)
|| !data->valid) {
int i;
dev_dbg(&client->dev, "Starting lm75 update\n");
for (i = 0; i < ARRAY_SIZE(data->temp); i++) {
int status;
status = lm75_read_value(client, LM75_REG_TEMP[i]);
if (unlikely(status < 0)) {
dev_dbg(dev,
"LM75: Failed to read value: reg %d, error %d\n",
LM75_REG_TEMP[i], status);
ret = ERR_PTR(status);
data->valid = 0;
goto abort;
}
data->temp[i] = status;
}
data->last_updated = jiffies;
data->valid = 1;
}
abort:
mutex_unlock(&data->update_lock);
return ret;
}
module_i2c_driver(lm75_driver);
MODULE_AUTHOR("Frodo Looijaard <frodol@dds.nl>");

View File

@ -171,7 +171,6 @@ enum chips { lm90, adm1032, lm99, lm86, max6657, max6659, adt7461, max6680,
#define SA56004_REG_R_LOCAL_TEMPL 0x22
#define LM90_DEF_CONVRATE_RVAL 6 /* Def conversion rate register value */
#define LM90_MAX_CONVRATE_MS 16000 /* Maximum conversion rate in ms */
/* TMP451 registers */
@ -366,11 +365,9 @@ enum lm90_temp11_reg_index {
struct lm90_data {
struct i2c_client *client;
struct device *hwmon_dev;
const struct attribute_group *groups[6];
struct mutex update_lock;
struct regulator *regulator;
char valid; /* zero until following fields are valid */
bool valid; /* true if register values are valid */
unsigned long last_updated; /* in jiffies */
int kind;
u32 flags;
@ -412,7 +409,7 @@ static inline s32 adm1032_write_byte(struct i2c_client *client, u8 value)
* because we don't want the address pointer to change between the write
* byte and the read byte transactions.
*/
static int lm90_read_reg(struct i2c_client *client, u8 reg, u8 *value)
static int lm90_read_reg(struct i2c_client *client, u8 reg)
{
int err;
@ -423,20 +420,12 @@ static int lm90_read_reg(struct i2c_client *client, u8 reg, u8 *value)
} else
err = i2c_smbus_read_byte_data(client, reg);
if (err < 0) {
dev_warn(&client->dev, "Register %#02x read failed (%d)\n",
reg, err);
return err;
}
*value = err;
return 0;
return err;
}
static int lm90_read16(struct i2c_client *client, u8 regh, u8 regl, u16 *value)
static int lm90_read16(struct i2c_client *client, u8 regh, u8 regl)
{
int err;
u8 oldh, newh, l;
int oldh, newh, l;
/*
* There is a trick here. We have to read two registers to have the
@ -451,18 +440,21 @@ static int lm90_read16(struct i2c_client *client, u8 regh, u8 regl, u16 *value)
* we have to read the low byte again, and now we believe we have a
* correct reading.
*/
if ((err = lm90_read_reg(client, regh, &oldh))
|| (err = lm90_read_reg(client, regl, &l))
|| (err = lm90_read_reg(client, regh, &newh)))
return err;
oldh = lm90_read_reg(client, regh);
if (oldh < 0)
return oldh;
l = lm90_read_reg(client, regl);
if (l < 0)
return l;
newh = lm90_read_reg(client, regh);
if (newh < 0)
return newh;
if (oldh != newh) {
err = lm90_read_reg(client, regl, &l);
if (err)
return err;
l = lm90_read_reg(client, regl);
if (l < 0)
return l;
}
*value = (newh << 8) | l;
return 0;
return (newh << 8) | l;
}
/*
@ -473,20 +465,23 @@ static int lm90_read16(struct i2c_client *client, u8 regh, u8 regl, u16 *value)
* various registers have different meanings as a result of selecting a
* non-default remote channel.
*/
static inline void lm90_select_remote_channel(struct i2c_client *client,
struct lm90_data *data,
int channel)
static inline int lm90_select_remote_channel(struct i2c_client *client,
struct lm90_data *data,
int channel)
{
u8 config;
int config;
if (data->kind == max6696) {
lm90_read_reg(client, LM90_REG_R_CONFIG1, &config);
config = lm90_read_reg(client, LM90_REG_R_CONFIG1);
if (config < 0)
return config;
config &= ~0x08;
if (channel)
config |= 0x08;
i2c_smbus_write_byte_data(client, LM90_REG_W_CONFIG1,
config);
}
return 0;
}
/*
@ -513,118 +508,204 @@ static void lm90_set_convrate(struct i2c_client *client, struct lm90_data *data,
data->update_interval = DIV_ROUND_CLOSEST(update_interval, 64);
}
static int lm90_update_limits(struct device *dev)
{
struct lm90_data *data = dev_get_drvdata(dev);
struct i2c_client *client = data->client;
int val;
val = lm90_read_reg(client, LM90_REG_R_LOCAL_CRIT);
if (val < 0)
return val;
data->temp8[LOCAL_CRIT] = val;
val = lm90_read_reg(client, LM90_REG_R_REMOTE_CRIT);
if (val < 0)
return val;
data->temp8[REMOTE_CRIT] = val;
val = lm90_read_reg(client, LM90_REG_R_TCRIT_HYST);
if (val < 0)
return val;
data->temp_hyst = val;
lm90_read_reg(client, LM90_REG_R_REMOTE_LOWH);
if (val < 0)
return val;
data->temp11[REMOTE_LOW] = val << 8;
if (data->flags & LM90_HAVE_REM_LIMIT_EXT) {
val = lm90_read_reg(client, LM90_REG_R_REMOTE_LOWL);
if (val < 0)
return val;
data->temp11[REMOTE_LOW] |= val;
}
val = lm90_read_reg(client, LM90_REG_R_REMOTE_HIGHH);
if (val < 0)
return val;
data->temp11[REMOTE_HIGH] = val << 8;
if (data->flags & LM90_HAVE_REM_LIMIT_EXT) {
val = lm90_read_reg(client, LM90_REG_R_REMOTE_HIGHL);
if (val < 0)
return val;
data->temp11[REMOTE_HIGH] |= val;
}
if (data->flags & LM90_HAVE_OFFSET) {
val = lm90_read16(client, LM90_REG_R_REMOTE_OFFSH,
LM90_REG_R_REMOTE_OFFSL);
if (val < 0)
return val;
data->temp11[REMOTE_OFFSET] = val;
}
if (data->flags & LM90_HAVE_EMERGENCY) {
val = lm90_read_reg(client, MAX6659_REG_R_LOCAL_EMERG);
if (val < 0)
return val;
data->temp8[LOCAL_EMERG] = val;
val = lm90_read_reg(client, MAX6659_REG_R_REMOTE_EMERG);
if (val < 0)
return val;
data->temp8[REMOTE_EMERG] = val;
}
if (data->kind == max6696) {
val = lm90_select_remote_channel(client, data, 1);
if (val < 0)
return val;
val = lm90_read_reg(client, LM90_REG_R_REMOTE_CRIT);
if (val < 0)
return val;
data->temp8[REMOTE2_CRIT] = val;
val = lm90_read_reg(client, MAX6659_REG_R_REMOTE_EMERG);
if (val < 0)
return val;
data->temp8[REMOTE2_EMERG] = val;
val = lm90_read_reg(client, LM90_REG_R_REMOTE_LOWH);
if (val < 0)
return val;
data->temp11[REMOTE2_LOW] = val << 8;
val = lm90_read_reg(client, LM90_REG_R_REMOTE_HIGHH);
if (val < 0)
return val;
data->temp11[REMOTE2_HIGH] = val << 8;
lm90_select_remote_channel(client, data, 0);
}
return 0;
}
static struct lm90_data *lm90_update_device(struct device *dev)
{
struct lm90_data *data = dev_get_drvdata(dev);
struct i2c_client *client = data->client;
unsigned long next_update;
int val = 0;
mutex_lock(&data->update_lock);
if (!data->valid) {
val = lm90_update_limits(dev);
if (val < 0)
goto error;
}
next_update = data->last_updated +
msecs_to_jiffies(data->update_interval);
if (time_after(jiffies, next_update) || !data->valid) {
u8 h, l;
u8 alarms;
dev_dbg(&client->dev, "Updating lm90 data.\n");
lm90_read_reg(client, LM90_REG_R_LOCAL_LOW,
&data->temp8[LOCAL_LOW]);
lm90_read_reg(client, LM90_REG_R_LOCAL_HIGH,
&data->temp8[LOCAL_HIGH]);
lm90_read_reg(client, LM90_REG_R_LOCAL_CRIT,
&data->temp8[LOCAL_CRIT]);
lm90_read_reg(client, LM90_REG_R_REMOTE_CRIT,
&data->temp8[REMOTE_CRIT]);
lm90_read_reg(client, LM90_REG_R_TCRIT_HYST, &data->temp_hyst);
data->valid = false;
val = lm90_read_reg(client, LM90_REG_R_LOCAL_LOW);
if (val < 0)
goto error;
data->temp8[LOCAL_LOW] = val;
val = lm90_read_reg(client, LM90_REG_R_LOCAL_HIGH);
if (val < 0)
goto error;
data->temp8[LOCAL_HIGH] = val;
if (data->reg_local_ext) {
lm90_read16(client, LM90_REG_R_LOCAL_TEMP,
data->reg_local_ext,
&data->temp11[LOCAL_TEMP]);
val = lm90_read16(client, LM90_REG_R_LOCAL_TEMP,
data->reg_local_ext);
if (val < 0)
goto error;
data->temp11[LOCAL_TEMP] = val;
} else {
if (lm90_read_reg(client, LM90_REG_R_LOCAL_TEMP,
&h) == 0)
data->temp11[LOCAL_TEMP] = h << 8;
val = lm90_read_reg(client, LM90_REG_R_LOCAL_TEMP);
if (val < 0)
goto error;
data->temp11[LOCAL_TEMP] = val << 8;
}
lm90_read16(client, LM90_REG_R_REMOTE_TEMPH,
LM90_REG_R_REMOTE_TEMPL,
&data->temp11[REMOTE_TEMP]);
val = lm90_read16(client, LM90_REG_R_REMOTE_TEMPH,
LM90_REG_R_REMOTE_TEMPL);
if (val < 0)
goto error;
data->temp11[REMOTE_TEMP] = val;
if (lm90_read_reg(client, LM90_REG_R_REMOTE_LOWH, &h) == 0) {
data->temp11[REMOTE_LOW] = h << 8;
if ((data->flags & LM90_HAVE_REM_LIMIT_EXT)
&& lm90_read_reg(client, LM90_REG_R_REMOTE_LOWL,
&l) == 0)
data->temp11[REMOTE_LOW] |= l;
}
if (lm90_read_reg(client, LM90_REG_R_REMOTE_HIGHH, &h) == 0) {
data->temp11[REMOTE_HIGH] = h << 8;
if ((data->flags & LM90_HAVE_REM_LIMIT_EXT)
&& lm90_read_reg(client, LM90_REG_R_REMOTE_HIGHL,
&l) == 0)
data->temp11[REMOTE_HIGH] |= l;
}
if (data->flags & LM90_HAVE_OFFSET) {
if (lm90_read_reg(client, LM90_REG_R_REMOTE_OFFSH,
&h) == 0
&& lm90_read_reg(client, LM90_REG_R_REMOTE_OFFSL,
&l) == 0)
data->temp11[REMOTE_OFFSET] = (h << 8) | l;
}
if (data->flags & LM90_HAVE_EMERGENCY) {
lm90_read_reg(client, MAX6659_REG_R_LOCAL_EMERG,
&data->temp8[LOCAL_EMERG]);
lm90_read_reg(client, MAX6659_REG_R_REMOTE_EMERG,
&data->temp8[REMOTE_EMERG]);
}
lm90_read_reg(client, LM90_REG_R_STATUS, &alarms);
data->alarms = alarms; /* save as 16 bit value */
val = lm90_read_reg(client, LM90_REG_R_STATUS);
if (val < 0)
goto error;
data->alarms = val; /* lower 8 bit of alarms */
if (data->kind == max6696) {
lm90_select_remote_channel(client, data, 1);
lm90_read_reg(client, LM90_REG_R_REMOTE_CRIT,
&data->temp8[REMOTE2_CRIT]);
lm90_read_reg(client, MAX6659_REG_R_REMOTE_EMERG,
&data->temp8[REMOTE2_EMERG]);
lm90_read16(client, LM90_REG_R_REMOTE_TEMPH,
LM90_REG_R_REMOTE_TEMPL,
&data->temp11[REMOTE2_TEMP]);
if (!lm90_read_reg(client, LM90_REG_R_REMOTE_LOWH, &h))
data->temp11[REMOTE2_LOW] = h << 8;
if (!lm90_read_reg(client, LM90_REG_R_REMOTE_HIGHH, &h))
data->temp11[REMOTE2_HIGH] = h << 8;
val = lm90_select_remote_channel(client, data, 1);
if (val < 0)
goto error;
val = lm90_read16(client, LM90_REG_R_REMOTE_TEMPH,
LM90_REG_R_REMOTE_TEMPL);
if (val < 0)
goto error;
data->temp11[REMOTE2_TEMP] = val;
lm90_select_remote_channel(client, data, 0);
if (!lm90_read_reg(client, MAX6696_REG_R_STATUS2,
&alarms))
data->alarms |= alarms << 8;
val = lm90_read_reg(client, MAX6696_REG_R_STATUS2);
if (val < 0)
goto error;
data->alarms |= val << 8;
}
/*
* Re-enable ALERT# output if it was originally enabled and
* relevant alarms are all clear
*/
if ((data->config_orig & 0x80) == 0
&& (data->alarms & data->alert_alarms) == 0) {
u8 config;
if (!(data->config_orig & 0x80) &&
!(data->alarms & data->alert_alarms)) {
val = lm90_read_reg(client, LM90_REG_R_CONFIG1);
if (val < 0)
goto error;
lm90_read_reg(client, LM90_REG_R_CONFIG1, &config);
if (config & 0x80) {
if (val & 0x80) {
dev_dbg(&client->dev, "Re-enabling ALERT#\n");
i2c_smbus_write_byte_data(client,
LM90_REG_W_CONFIG1,
config & ~0x80);
val & ~0x80);
}
}
data->last_updated = jiffies;
data->valid = 1;
data->valid = true;
}
error:
mutex_unlock(&data->update_lock);
if (val < 0)
return ERR_PTR(val);
return data;
}
@ -709,16 +790,14 @@ static inline int temp_from_u8_adt7461(struct lm90_data *data, u8 val)
{
if (data->flags & LM90_FLAG_ADT7461_EXT)
return (val - 64) * 1000;
else
return temp_from_s8(val);
return temp_from_s8(val);
}
static inline int temp_from_u16_adt7461(struct lm90_data *data, u16 val)
{
if (data->flags & LM90_FLAG_ADT7461_EXT)
return (val - 0x4000) / 64 * 250;
else
return temp_from_s16(val);
return temp_from_s16(val);
}
static u8 temp_to_u8_adt7461(struct lm90_data *data, long val)
@ -729,13 +808,12 @@ static u8 temp_to_u8_adt7461(struct lm90_data *data, long val)
if (val >= 191000)
return 0xFF;
return (val + 500 + 64000) / 1000;
} else {
if (val <= 0)
return 0;
if (val >= 127000)
return 127;
return (val + 500) / 1000;
}
if (val <= 0)
return 0;
if (val >= 127000)
return 127;
return (val + 500) / 1000;
}
static u16 temp_to_u16_adt7461(struct lm90_data *data, long val)
@ -746,13 +824,12 @@ static u16 temp_to_u16_adt7461(struct lm90_data *data, long val)
if (val >= 191750)
return 0xFFC0;
return (val + 64000 + 125) / 250 * 64;
} else {
if (val <= 0)
return 0;
if (val >= 127750)
return 0x7FC0;
return (val + 125) / 250 * 64;
}
if (val <= 0)
return 0;
if (val >= 127750)
return 0x7FC0;
return (val + 125) / 250 * 64;
}
/*
@ -766,6 +843,9 @@ static ssize_t show_temp8(struct device *dev, struct device_attribute *devattr,
struct lm90_data *data = lm90_update_device(dev);
int temp;
if (IS_ERR(data))
return PTR_ERR(data);
if (data->kind == adt7461 || data->kind == tmp451)
temp = temp_from_u8_adt7461(data, data->temp8[attr->index]);
else if (data->kind == max6646)
@ -832,6 +912,9 @@ static ssize_t show_temp11(struct device *dev, struct device_attribute *devattr,
struct lm90_data *data = lm90_update_device(dev);
int temp;
if (IS_ERR(data))
return PTR_ERR(data);
if (data->kind == adt7461 || data->kind == tmp451)
temp = temp_from_u16_adt7461(data, data->temp11[attr->index]);
else if (data->kind == max6646)
@ -907,6 +990,9 @@ static ssize_t show_temphyst(struct device *dev,
struct lm90_data *data = lm90_update_device(dev);
int temp;
if (IS_ERR(data))
return PTR_ERR(data);
if (data->kind == adt7461 || data->kind == tmp451)
temp = temp_from_u8_adt7461(data, data->temp8[attr->index]);
else if (data->kind == max6646)
@ -953,6 +1039,10 @@ static ssize_t show_alarms(struct device *dev, struct device_attribute *dummy,
char *buf)
{
struct lm90_data *data = lm90_update_device(dev);
if (IS_ERR(data))
return PTR_ERR(data);
return sprintf(buf, "%d\n", data->alarms);
}
@ -963,6 +1053,9 @@ static ssize_t show_alarm(struct device *dev, struct device_attribute
struct lm90_data *data = lm90_update_device(dev);
int bitnr = attr->index;
if (IS_ERR(data))
return PTR_ERR(data);
return sprintf(buf, "%d\n", (data->alarms >> bitnr) & 1);
}
@ -1404,8 +1497,11 @@ static int lm90_detect(struct i2c_client *client,
return 0;
}
static void lm90_restore_conf(struct i2c_client *client, struct lm90_data *data)
static void lm90_restore_conf(void *_data)
{
struct lm90_data *data = _data;
struct i2c_client *client = data->client;
/* Restore initial configuration */
i2c_smbus_write_byte_data(client, LM90_REG_W_CONVRATE,
data->convrate_orig);
@ -1413,24 +1509,22 @@ static void lm90_restore_conf(struct i2c_client *client, struct lm90_data *data)
data->config_orig);
}
static void lm90_init_client(struct i2c_client *client, struct lm90_data *data)
static int lm90_init_client(struct i2c_client *client, struct lm90_data *data)
{
u8 config, convrate;
int config, convrate;
if (lm90_read_reg(client, LM90_REG_R_CONVRATE, &convrate) < 0) {
dev_warn(&client->dev, "Failed to read convrate register!\n");
convrate = LM90_DEF_CONVRATE_RVAL;
}
convrate = lm90_read_reg(client, LM90_REG_R_CONVRATE);
if (convrate < 0)
return convrate;
data->convrate_orig = convrate;
/*
* Start the conversions.
*/
lm90_set_convrate(client, data, 500); /* 500ms; 2Hz conversion rate */
if (lm90_read_reg(client, LM90_REG_R_CONFIG1, &config) < 0) {
dev_warn(&client->dev, "Initialization failed!\n");
return;
}
config = lm90_read_reg(client, LM90_REG_R_CONFIG1);
if (config < 0)
return config;
data->config_orig = config;
/* Check Temperature Range Select */
@ -1456,17 +1550,26 @@ static void lm90_init_client(struct i2c_client *client, struct lm90_data *data)
config &= 0xBF; /* run */
if (config != data->config_orig) /* Only write if changed */
i2c_smbus_write_byte_data(client, LM90_REG_W_CONFIG1, config);
devm_add_action(&client->dev, lm90_restore_conf, data);
return 0;
}
static bool lm90_is_tripped(struct i2c_client *client, u16 *status)
{
struct lm90_data *data = i2c_get_clientdata(client);
u8 st, st2 = 0;
int st, st2 = 0;
lm90_read_reg(client, LM90_REG_R_STATUS, &st);
st = lm90_read_reg(client, LM90_REG_R_STATUS);
if (st < 0)
return false;
if (data->kind == max6696)
lm90_read_reg(client, MAX6696_REG_R_STATUS2, &st2);
if (data->kind == max6696) {
st2 = lm90_read_reg(client, MAX6696_REG_R_STATUS2);
if (st2 < 0)
return false;
}
*status = st | (st2 << 8);
@ -1506,6 +1609,16 @@ static irqreturn_t lm90_irq_thread(int irq, void *dev_id)
return IRQ_NONE;
}
static void lm90_remove_pec(void *dev)
{
device_remove_file(dev, &dev_attr_pec);
}
static void lm90_regulator_disable(void *regulator)
{
regulator_disable(regulator);
}
static int lm90_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
@ -1513,6 +1626,7 @@ static int lm90_probe(struct i2c_client *client,
struct i2c_adapter *adapter = to_i2c_adapter(dev->parent);
struct lm90_data *data;
struct regulator *regulator;
struct device *hwmon_dev;
int groups = 0;
int err;
@ -1526,6 +1640,8 @@ static int lm90_probe(struct i2c_client *client,
return err;
}
devm_add_action(dev, lm90_regulator_disable, regulator);
data = devm_kzalloc(dev, sizeof(struct lm90_data), GFP_KERNEL);
if (!data)
return -ENOMEM;
@ -1534,8 +1650,6 @@ static int lm90_probe(struct i2c_client *client,
i2c_set_clientdata(client, data);
mutex_init(&data->update_lock);
data->regulator = regulator;
/* Set the device type */
data->kind = id->driver_data;
if (data->kind == adm1032) {
@ -1557,7 +1671,11 @@ static int lm90_probe(struct i2c_client *client,
data->max_convrate = lm90_params[data->kind].max_convrate;
/* Initialize the LM90 chip */
lm90_init_client(client, data);
err = lm90_init_client(client, data);
if (err < 0) {
dev_err(dev, "Failed to initialize device\n");
return err;
}
/* Register sysfs hooks */
data->groups[groups++] = &lm90_group;
@ -1577,15 +1695,14 @@ static int lm90_probe(struct i2c_client *client,
if (client->flags & I2C_CLIENT_PEC) {
err = device_create_file(dev, &dev_attr_pec);
if (err)
goto exit_restore;
return err;
devm_add_action(dev, lm90_remove_pec, dev);
}
data->hwmon_dev = hwmon_device_register_with_groups(dev, client->name,
data, data->groups);
if (IS_ERR(data->hwmon_dev)) {
err = PTR_ERR(data->hwmon_dev);
goto exit_remove_pec;
}
hwmon_dev = devm_hwmon_device_register_with_groups(dev, client->name,
data, data->groups);
if (IS_ERR(hwmon_dev))
return PTR_ERR(hwmon_dev);
if (client->irq) {
dev_dbg(dev, "IRQ: %d\n", client->irq);
@ -1595,32 +1712,10 @@ static int lm90_probe(struct i2c_client *client,
"lm90", client);
if (err < 0) {
dev_err(dev, "cannot request IRQ %d\n", client->irq);
goto exit_unregister;
return err;
}
}
return 0;
exit_unregister:
hwmon_device_unregister(data->hwmon_dev);
exit_remove_pec:
device_remove_file(dev, &dev_attr_pec);
exit_restore:
lm90_restore_conf(client, data);
regulator_disable(data->regulator);
return err;
}
static int lm90_remove(struct i2c_client *client)
{
struct lm90_data *data = i2c_get_clientdata(client);
hwmon_device_unregister(data->hwmon_dev);
device_remove_file(&client->dev, &dev_attr_pec);
lm90_restore_conf(client, data);
regulator_disable(data->regulator);
return 0;
}
@ -1636,13 +1731,16 @@ static void lm90_alert(struct i2c_client *client, unsigned int flag)
*/
struct lm90_data *data = i2c_get_clientdata(client);
if ((data->flags & LM90_HAVE_BROKEN_ALERT)
&& (alarms & data->alert_alarms)) {
u8 config;
if ((data->flags & LM90_HAVE_BROKEN_ALERT) &&
(alarms & data->alert_alarms)) {
int config;
dev_dbg(&client->dev, "Disabling ALERT#\n");
lm90_read_reg(client, LM90_REG_R_CONFIG1, &config);
i2c_smbus_write_byte_data(client, LM90_REG_W_CONFIG1,
config | 0x80);
config = lm90_read_reg(client, LM90_REG_R_CONFIG1);
if (config >= 0)
i2c_smbus_write_byte_data(client,
LM90_REG_W_CONFIG1,
config | 0x80);
}
} else {
dev_info(&client->dev, "Everything OK\n");
@ -1655,7 +1753,6 @@ static struct i2c_driver lm90_driver = {
.name = "lm90",
},
.probe = lm90_probe,
.remove = lm90_remove,
.alert = lm90_alert,
.id_table = lm90_id,
.detect = lm90_detect,

775
drivers/hwmon/sht3x.c Normal file
View File

@ -0,0 +1,775 @@
/* Sensirion SHT3x-DIS humidity and temperature sensor driver.
* The SHT3x comes in many different versions, this driver is for the
* I2C version only.
*
* Copyright (C) 2016 Sensirion AG, Switzerland
* Author: David Frey <david.frey@sensirion.com>
* Author: Pascal Sachs <pascal.sachs@sensirion.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*/
#include <asm/page.h>
#include <linux/crc8.h>
#include <linux/delay.h>
#include <linux/err.h>
#include <linux/hwmon.h>
#include <linux/hwmon-sysfs.h>
#include <linux/i2c.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/jiffies.h>
#include <linux/platform_data/sht3x.h>
/* commands (high precision mode) */
static const unsigned char sht3x_cmd_measure_blocking_hpm[] = { 0x2c, 0x06 };
static const unsigned char sht3x_cmd_measure_nonblocking_hpm[] = { 0x24, 0x00 };
/* commands (low power mode) */
static const unsigned char sht3x_cmd_measure_blocking_lpm[] = { 0x2c, 0x10 };
static const unsigned char sht3x_cmd_measure_nonblocking_lpm[] = { 0x24, 0x16 };
/* commands for periodic mode */
static const unsigned char sht3x_cmd_measure_periodic_mode[] = { 0xe0, 0x00 };
static const unsigned char sht3x_cmd_break[] = { 0x30, 0x93 };
/* commands for heater control */
static const unsigned char sht3x_cmd_heater_on[] = { 0x30, 0x6d };
static const unsigned char sht3x_cmd_heater_off[] = { 0x30, 0x66 };
/* other commands */
static const unsigned char sht3x_cmd_read_status_reg[] = { 0xf3, 0x2d };
static const unsigned char sht3x_cmd_clear_status_reg[] = { 0x30, 0x41 };
/* delays for non-blocking i2c commands, both in us */
#define SHT3X_NONBLOCKING_WAIT_TIME_HPM 15000
#define SHT3X_NONBLOCKING_WAIT_TIME_LPM 4000
#define SHT3X_WORD_LEN 2
#define SHT3X_CMD_LENGTH 2
#define SHT3X_CRC8_LEN 1
#define SHT3X_RESPONSE_LENGTH 6
#define SHT3X_CRC8_POLYNOMIAL 0x31
#define SHT3X_CRC8_INIT 0xFF
#define SHT3X_MIN_TEMPERATURE -45000
#define SHT3X_MAX_TEMPERATURE 130000
#define SHT3X_MIN_HUMIDITY 0
#define SHT3X_MAX_HUMIDITY 100000
enum sht3x_chips {
sht3x,
sts3x,
};
enum sht3x_limits {
limit_max = 0,
limit_max_hyst,
limit_min,
limit_min_hyst,
};
DECLARE_CRC8_TABLE(sht3x_crc8_table);
/* periodic measure commands (high precision mode) */
static const char periodic_measure_commands_hpm[][SHT3X_CMD_LENGTH] = {
/* 0.5 measurements per second */
{0x20, 0x32},
/* 1 measurements per second */
{0x21, 0x30},
/* 2 measurements per second */
{0x22, 0x36},
/* 4 measurements per second */
{0x23, 0x34},
/* 10 measurements per second */
{0x27, 0x37},
};
/* periodic measure commands (low power mode) */
static const char periodic_measure_commands_lpm[][SHT3X_CMD_LENGTH] = {
/* 0.5 measurements per second */
{0x20, 0x2f},
/* 1 measurements per second */
{0x21, 0x2d},
/* 2 measurements per second */
{0x22, 0x2b},
/* 4 measurements per second */
{0x23, 0x29},
/* 10 measurements per second */
{0x27, 0x2a},
};
struct sht3x_limit_commands {
const char read_command[SHT3X_CMD_LENGTH];
const char write_command[SHT3X_CMD_LENGTH];
};
static const struct sht3x_limit_commands limit_commands[] = {
/* temp1_max, humidity1_max */
[limit_max] = { {0xe1, 0x1f}, {0x61, 0x1d} },
/* temp_1_max_hyst, humidity1_max_hyst */
[limit_max_hyst] = { {0xe1, 0x14}, {0x61, 0x16} },
/* temp1_min, humidity1_min */
[limit_min] = { {0xe1, 0x02}, {0x61, 0x00} },
/* temp_1_min_hyst, humidity1_min_hyst */
[limit_min_hyst] = { {0xe1, 0x09}, {0x61, 0x0B} },
};
#define SHT3X_NUM_LIMIT_CMD ARRAY_SIZE(limit_commands)
static const u16 mode_to_update_interval[] = {
0,
2000,
1000,
500,
250,
100,
};
struct sht3x_data {
struct i2c_client *client;
struct mutex i2c_lock; /* lock for sending i2c commands */
struct mutex data_lock; /* lock for updating driver data */
u8 mode;
const unsigned char *command;
u32 wait_time; /* in us*/
unsigned long last_update; /* last update in periodic mode*/
struct sht3x_platform_data setup;
/*
* cached values for temperature and humidity and limits
* the limits arrays have the following order:
* max, max_hyst, min, min_hyst
*/
int temperature;
int temperature_limits[SHT3X_NUM_LIMIT_CMD];
u32 humidity;
u32 humidity_limits[SHT3X_NUM_LIMIT_CMD];
};
static u8 get_mode_from_update_interval(u16 value)
{
size_t index;
u8 number_of_modes = ARRAY_SIZE(mode_to_update_interval);
if (value == 0)
return 0;
/* find next faster update interval */
for (index = 1; index < number_of_modes; index++) {
if (mode_to_update_interval[index] <= value)
return index;
}
return number_of_modes - 1;
}
static int sht3x_read_from_command(struct i2c_client *client,
struct sht3x_data *data,
const char *command,
char *buf, int length, u32 wait_time)
{
int ret;
mutex_lock(&data->i2c_lock);
ret = i2c_master_send(client, command, SHT3X_CMD_LENGTH);
if (ret != SHT3X_CMD_LENGTH) {
ret = ret < 0 ? ret : -EIO;
goto out;
}
if (wait_time)
usleep_range(wait_time, wait_time + 1000);
ret = i2c_master_recv(client, buf, length);
if (ret != length) {
ret = ret < 0 ? ret : -EIO;
goto out;
}
ret = 0;
out:
mutex_unlock(&data->i2c_lock);
return ret;
}
static int sht3x_extract_temperature(u16 raw)
{
/*
* From datasheet:
* T = -45 + 175 * ST / 2^16
* Adapted for integer fixed point (3 digit) arithmetic.
*/
return ((21875 * (int)raw) >> 13) - 45000;
}
static u32 sht3x_extract_humidity(u16 raw)
{
/*
* From datasheet:
* RH = 100 * SRH / 2^16
* Adapted for integer fixed point (3 digit) arithmetic.
*/
return (12500 * (u32)raw) >> 13;
}
static struct sht3x_data *sht3x_update_client(struct device *dev)
{
struct sht3x_data *data = dev_get_drvdata(dev);
struct i2c_client *client = data->client;
u16 interval_ms = mode_to_update_interval[data->mode];
unsigned long interval_jiffies = msecs_to_jiffies(interval_ms);
unsigned char buf[SHT3X_RESPONSE_LENGTH];
u16 val;
int ret = 0;
mutex_lock(&data->data_lock);
/*
* Only update cached readings once per update interval in periodic
* mode. In single shot mode the sensor measures values on demand, so
* every time the sysfs interface is called, a measurement is triggered.
* In periodic mode however, the measurement process is handled
* internally by the sensor and reading out sensor values only makes
* sense if a new reading is available.
*/
if (time_after(jiffies, data->last_update + interval_jiffies)) {
ret = sht3x_read_from_command(client, data, data->command, buf,
sizeof(buf), data->wait_time);
if (ret)
goto out;
val = be16_to_cpup((__be16 *)buf);
data->temperature = sht3x_extract_temperature(val);
val = be16_to_cpup((__be16 *)(buf + 3));
data->humidity = sht3x_extract_humidity(val);
data->last_update = jiffies;
}
out:
mutex_unlock(&data->data_lock);
if (ret)
return ERR_PTR(ret);
return data;
}
/* sysfs attributes */
static ssize_t temp1_input_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct sht3x_data *data = sht3x_update_client(dev);
if (IS_ERR(data))
return PTR_ERR(data);
return sprintf(buf, "%d\n", data->temperature);
}
static ssize_t humidity1_input_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct sht3x_data *data = sht3x_update_client(dev);
if (IS_ERR(data))
return PTR_ERR(data);
return sprintf(buf, "%u\n", data->humidity);
}
/*
* limits_update must only be called from probe or with data_lock held
*/
static int limits_update(struct sht3x_data *data)
{
int ret;
u8 index;
int temperature;
u32 humidity;
u16 raw;
char buffer[SHT3X_RESPONSE_LENGTH];
const struct sht3x_limit_commands *commands;
struct i2c_client *client = data->client;
for (index = 0; index < SHT3X_NUM_LIMIT_CMD; index++) {
commands = &limit_commands[index];
ret = sht3x_read_from_command(client, data,
commands->read_command, buffer,
SHT3X_RESPONSE_LENGTH, 0);
if (ret)
return ret;
raw = be16_to_cpup((__be16 *)buffer);
temperature = sht3x_extract_temperature((raw & 0x01ff) << 7);
humidity = sht3x_extract_humidity(raw & 0xfe00);
data->temperature_limits[index] = temperature;
data->humidity_limits[index] = humidity;
}
return ret;
}
static ssize_t temp1_limit_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct sht3x_data *data = dev_get_drvdata(dev);
u8 index = to_sensor_dev_attr(attr)->index;
int temperature_limit = data->temperature_limits[index];
return scnprintf(buf, PAGE_SIZE, "%d\n", temperature_limit);
}
static ssize_t humidity1_limit_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct sht3x_data *data = dev_get_drvdata(dev);
u8 index = to_sensor_dev_attr(attr)->index;
u32 humidity_limit = data->humidity_limits[index];
return scnprintf(buf, PAGE_SIZE, "%u\n", humidity_limit);
}
/*
* limit_store must only be called with data_lock held
*/
static size_t limit_store(struct device *dev,
size_t count,
u8 index,
int temperature,
u32 humidity)
{
char buffer[SHT3X_CMD_LENGTH + SHT3X_WORD_LEN + SHT3X_CRC8_LEN];
char *position = buffer;
int ret;
u16 raw;
struct sht3x_data *data = dev_get_drvdata(dev);
struct i2c_client *client = data->client;
const struct sht3x_limit_commands *commands;
commands = &limit_commands[index];
memcpy(position, commands->write_command, SHT3X_CMD_LENGTH);
position += SHT3X_CMD_LENGTH;
/*
* ST = (T + 45) / 175 * 2^16
* SRH = RH / 100 * 2^16
* adapted for fixed point arithmetic and packed the same as
* in limit_show()
*/
raw = ((u32)(temperature + 45000) * 24543) >> (16 + 7);
raw |= ((humidity * 42950) >> 16) & 0xfe00;
*((__be16 *)position) = cpu_to_be16(raw);
position += SHT3X_WORD_LEN;
*position = crc8(sht3x_crc8_table,
position - SHT3X_WORD_LEN,
SHT3X_WORD_LEN,
SHT3X_CRC8_INIT);
mutex_lock(&data->i2c_lock);
ret = i2c_master_send(client, buffer, sizeof(buffer));
mutex_unlock(&data->i2c_lock);
if (ret != sizeof(buffer))
return ret < 0 ? ret : -EIO;
data->temperature_limits[index] = temperature;
data->humidity_limits[index] = humidity;
return count;
}
static ssize_t temp1_limit_store(struct device *dev,
struct device_attribute *attr,
const char *buf,
size_t count)
{
int temperature;
int ret;
struct sht3x_data *data = dev_get_drvdata(dev);
u8 index = to_sensor_dev_attr(attr)->index;
ret = kstrtoint(buf, 0, &temperature);
if (ret)
return ret;
temperature = clamp_val(temperature, SHT3X_MIN_TEMPERATURE,
SHT3X_MAX_TEMPERATURE);
mutex_lock(&data->data_lock);
ret = limit_store(dev, count, index, temperature,
data->humidity_limits[index]);
mutex_unlock(&data->data_lock);
return ret;
}
static ssize_t humidity1_limit_store(struct device *dev,
struct device_attribute *attr,
const char *buf,
size_t count)
{
u32 humidity;
int ret;
struct sht3x_data *data = dev_get_drvdata(dev);
u8 index = to_sensor_dev_attr(attr)->index;
ret = kstrtou32(buf, 0, &humidity);
if (ret)
return ret;
humidity = clamp_val(humidity, SHT3X_MIN_HUMIDITY, SHT3X_MAX_HUMIDITY);
mutex_lock(&data->data_lock);
ret = limit_store(dev, count, index, data->temperature_limits[index],
humidity);
mutex_unlock(&data->data_lock);
return ret;
}
static void sht3x_select_command(struct sht3x_data *data)
{
/*
* In blocking mode (clock stretching mode) the I2C bus
* is blocked for other traffic, thus the call to i2c_master_recv()
* will wait until the data is ready. For non blocking mode, we
* have to wait ourselves.
*/
if (data->mode > 0) {
data->command = sht3x_cmd_measure_periodic_mode;
data->wait_time = 0;
} else if (data->setup.blocking_io) {
data->command = data->setup.high_precision ?
sht3x_cmd_measure_blocking_hpm :
sht3x_cmd_measure_blocking_lpm;
data->wait_time = 0;
} else {
if (data->setup.high_precision) {
data->command = sht3x_cmd_measure_nonblocking_hpm;
data->wait_time = SHT3X_NONBLOCKING_WAIT_TIME_HPM;
} else {
data->command = sht3x_cmd_measure_nonblocking_lpm;
data->wait_time = SHT3X_NONBLOCKING_WAIT_TIME_LPM;
}
}
}
static int status_register_read(struct device *dev,
struct device_attribute *attr,
char *buffer, int length)
{
int ret;
struct sht3x_data *data = dev_get_drvdata(dev);
struct i2c_client *client = data->client;
ret = sht3x_read_from_command(client, data, sht3x_cmd_read_status_reg,
buffer, length, 0);
return ret;
}
static ssize_t temp1_alarm_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
char buffer[SHT3X_WORD_LEN + SHT3X_CRC8_LEN];
int ret;
ret = status_register_read(dev, attr, buffer,
SHT3X_WORD_LEN + SHT3X_CRC8_LEN);
if (ret)
return ret;
return scnprintf(buf, PAGE_SIZE, "%d\n", !!(buffer[0] & 0x04));
}
static ssize_t humidity1_alarm_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
char buffer[SHT3X_WORD_LEN + SHT3X_CRC8_LEN];
int ret;
ret = status_register_read(dev, attr, buffer,
SHT3X_WORD_LEN + SHT3X_CRC8_LEN);
if (ret)
return ret;
return scnprintf(buf, PAGE_SIZE, "%d\n", !!(buffer[0] & 0x08));
}
static ssize_t heater_enable_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
char buffer[SHT3X_WORD_LEN + SHT3X_CRC8_LEN];
int ret;
ret = status_register_read(dev, attr, buffer,
SHT3X_WORD_LEN + SHT3X_CRC8_LEN);
if (ret)
return ret;
return scnprintf(buf, PAGE_SIZE, "%d\n", !!(buffer[0] & 0x20));
}
static ssize_t heater_enable_store(struct device *dev,
struct device_attribute *attr,
const char *buf,
size_t count)
{
struct sht3x_data *data = dev_get_drvdata(dev);
struct i2c_client *client = data->client;
int ret;
bool status;
ret = kstrtobool(buf, &status);
if (ret)
return ret;
mutex_lock(&data->i2c_lock);
if (status)
ret = i2c_master_send(client, (char *)&sht3x_cmd_heater_on,
SHT3X_CMD_LENGTH);
else
ret = i2c_master_send(client, (char *)&sht3x_cmd_heater_off,
SHT3X_CMD_LENGTH);
mutex_unlock(&data->i2c_lock);
return ret;
}
static ssize_t update_interval_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct sht3x_data *data = dev_get_drvdata(dev);
return scnprintf(buf, PAGE_SIZE, "%u\n",
mode_to_update_interval[data->mode]);
}
static ssize_t update_interval_store(struct device *dev,
struct device_attribute *attr,
const char *buf,
size_t count)
{
u16 update_interval;
u8 mode;
int ret;
const char *command;
struct sht3x_data *data = dev_get_drvdata(dev);
struct i2c_client *client = data->client;
ret = kstrtou16(buf, 0, &update_interval);
if (ret)
return ret;
mode = get_mode_from_update_interval(update_interval);
mutex_lock(&data->data_lock);
/* mode did not change */
if (mode == data->mode) {
mutex_unlock(&data->data_lock);
return count;
}
mutex_lock(&data->i2c_lock);
/*
* Abort periodic measure mode.
* To do any changes to the configuration while in periodic mode, we
* have to send a break command to the sensor, which then falls back
* to single shot (mode = 0).
*/
if (data->mode > 0) {
ret = i2c_master_send(client, sht3x_cmd_break,
SHT3X_CMD_LENGTH);
if (ret != SHT3X_CMD_LENGTH)
goto out;
data->mode = 0;
}
if (mode > 0) {
if (data->setup.high_precision)
command = periodic_measure_commands_hpm[mode - 1];
else
command = periodic_measure_commands_lpm[mode - 1];
/* select mode */
ret = i2c_master_send(client, command, SHT3X_CMD_LENGTH);
if (ret != SHT3X_CMD_LENGTH)
goto out;
}
/* select mode and command */
data->mode = mode;
sht3x_select_command(data);
out:
mutex_unlock(&data->i2c_lock);
mutex_unlock(&data->data_lock);
if (ret != SHT3X_CMD_LENGTH)
return ret < 0 ? ret : -EIO;
return count;
}
static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, temp1_input_show, NULL, 0);
static SENSOR_DEVICE_ATTR(humidity1_input, S_IRUGO, humidity1_input_show,
NULL, 0);
static SENSOR_DEVICE_ATTR(temp1_max, S_IRUGO | S_IWUSR,
temp1_limit_show, temp1_limit_store,
limit_max);
static SENSOR_DEVICE_ATTR(humidity1_max, S_IRUGO | S_IWUSR,
humidity1_limit_show, humidity1_limit_store,
limit_max);
static SENSOR_DEVICE_ATTR(temp1_max_hyst, S_IRUGO | S_IWUSR,
temp1_limit_show, temp1_limit_store,
limit_max_hyst);
static SENSOR_DEVICE_ATTR(humidity1_max_hyst, S_IRUGO | S_IWUSR,
humidity1_limit_show, humidity1_limit_store,
limit_max_hyst);
static SENSOR_DEVICE_ATTR(temp1_min, S_IRUGO | S_IWUSR,
temp1_limit_show, temp1_limit_store,
limit_min);
static SENSOR_DEVICE_ATTR(humidity1_min, S_IRUGO | S_IWUSR,
humidity1_limit_show, humidity1_limit_store,
limit_min);
static SENSOR_DEVICE_ATTR(temp1_min_hyst, S_IRUGO | S_IWUSR,
temp1_limit_show, temp1_limit_store,
limit_min_hyst);
static SENSOR_DEVICE_ATTR(humidity1_min_hyst, S_IRUGO | S_IWUSR,
humidity1_limit_show, humidity1_limit_store,
limit_min_hyst);
static SENSOR_DEVICE_ATTR(temp1_alarm, S_IRUGO, temp1_alarm_show, NULL, 0);
static SENSOR_DEVICE_ATTR(humidity1_alarm, S_IRUGO, humidity1_alarm_show,
NULL, 0);
static SENSOR_DEVICE_ATTR(heater_enable, S_IRUGO | S_IWUSR,
heater_enable_show, heater_enable_store, 0);
static SENSOR_DEVICE_ATTR(update_interval, S_IRUGO | S_IWUSR,
update_interval_show, update_interval_store, 0);
static struct attribute *sht3x_attrs[] = {
&sensor_dev_attr_temp1_input.dev_attr.attr,
&sensor_dev_attr_humidity1_input.dev_attr.attr,
&sensor_dev_attr_temp1_max.dev_attr.attr,
&sensor_dev_attr_temp1_max_hyst.dev_attr.attr,
&sensor_dev_attr_humidity1_max.dev_attr.attr,
&sensor_dev_attr_humidity1_max_hyst.dev_attr.attr,
&sensor_dev_attr_temp1_min.dev_attr.attr,
&sensor_dev_attr_temp1_min_hyst.dev_attr.attr,
&sensor_dev_attr_humidity1_min.dev_attr.attr,
&sensor_dev_attr_humidity1_min_hyst.dev_attr.attr,
&sensor_dev_attr_temp1_alarm.dev_attr.attr,
&sensor_dev_attr_humidity1_alarm.dev_attr.attr,
&sensor_dev_attr_heater_enable.dev_attr.attr,
&sensor_dev_attr_update_interval.dev_attr.attr,
NULL
};
static struct attribute *sts3x_attrs[] = {
&sensor_dev_attr_temp1_input.dev_attr.attr,
NULL
};
ATTRIBUTE_GROUPS(sht3x);
ATTRIBUTE_GROUPS(sts3x);
static int sht3x_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
int ret;
struct sht3x_data *data;
struct device *hwmon_dev;
struct i2c_adapter *adap = client->adapter;
struct device *dev = &client->dev;
const struct attribute_group **attribute_groups;
/*
* we require full i2c support since the sht3x uses multi-byte read and
* writes as well as multi-byte commands which are not supported by
* the smbus protocol
*/
if (!i2c_check_functionality(adap, I2C_FUNC_I2C))
return -ENODEV;
ret = i2c_master_send(client, sht3x_cmd_clear_status_reg,
SHT3X_CMD_LENGTH);
if (ret != SHT3X_CMD_LENGTH)
return ret < 0 ? ret : -ENODEV;
data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
if (!data)
return -ENOMEM;
data->setup.blocking_io = false;
data->setup.high_precision = true;
data->mode = 0;
data->last_update = 0;
data->client = client;
crc8_populate_msb(sht3x_crc8_table, SHT3X_CRC8_POLYNOMIAL);
if (client->dev.platform_data)
data->setup = *(struct sht3x_platform_data *)dev->platform_data;
sht3x_select_command(data);
mutex_init(&data->i2c_lock);
mutex_init(&data->data_lock);
ret = limits_update(data);
if (ret)
return ret;
if (id->driver_data == sts3x)
attribute_groups = sts3x_groups;
else
attribute_groups = sht3x_groups;
hwmon_dev = devm_hwmon_device_register_with_groups(dev,
client->name,
data,
attribute_groups);
if (IS_ERR(hwmon_dev))
dev_dbg(dev, "unable to register hwmon device\n");
return PTR_ERR_OR_ZERO(hwmon_dev);
}
/* device ID table */
static const struct i2c_device_id sht3x_ids[] = {
{"sht3x", sht3x},
{"sts3x", sts3x},
{}
};
MODULE_DEVICE_TABLE(i2c, sht3x_ids);
static struct i2c_driver sht3x_i2c_driver = {
.driver.name = "sht3x",
.probe = sht3x_probe,
.id_table = sht3x_ids,
};
module_i2c_driver(sht3x_i2c_driver);
MODULE_AUTHOR("David Frey <david.frey@sensirion.com>");
MODULE_AUTHOR("Pascal Sachs <pascal.sachs@sensirion.com>");
MODULE_DESCRIPTION("Sensirion SHT3x humidity and temperature sensor driver");
MODULE_LICENSE("GPL");

View File

@ -11,12 +11,9 @@
* 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., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <linux/delay.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/slab.h>
@ -27,6 +24,7 @@
#include <linux/mutex.h>
#include <linux/device.h>
#include <linux/jiffies.h>
#include <linux/regmap.h>
#include <linux/thermal.h>
#include <linux/of.h>
@ -50,14 +48,23 @@
#define TMP102_TLOW_REG 0x02
#define TMP102_THIGH_REG 0x03
#define TMP102_CONFREG_MASK (TMP102_CONF_SD | TMP102_CONF_TM | \
TMP102_CONF_POL | TMP102_CONF_F0 | \
TMP102_CONF_F1 | TMP102_CONF_OS | \
TMP102_CONF_EM | TMP102_CONF_AL | \
TMP102_CONF_CR0 | TMP102_CONF_CR1)
#define TMP102_CONFIG_CLEAR (TMP102_CONF_SD | TMP102_CONF_OS | \
TMP102_CONF_CR0)
#define TMP102_CONFIG_SET (TMP102_CONF_TM | TMP102_CONF_EM | \
TMP102_CONF_CR1)
#define CONVERSION_TIME_MS 35 /* in milli-seconds */
struct tmp102 {
struct i2c_client *client;
struct device *hwmon_dev;
struct mutex lock;
struct regmap *regmap;
u16 config_orig;
unsigned long last_update;
int temp[3];
bool first_time;
unsigned long ready_time;
};
/* convert left adjusted 13-bit TMP102 register value to milliCelsius */
@ -72,44 +79,22 @@ static inline u16 tmp102_mC_to_reg(int val)
return (val * 128) / 1000;
}
static const u8 tmp102_reg[] = {
TMP102_TEMP_REG,
TMP102_TLOW_REG,
TMP102_THIGH_REG,
};
static struct tmp102 *tmp102_update_device(struct device *dev)
{
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)) {
int i;
for (i = 0; i < ARRAY_SIZE(tmp102->temp); ++i) {
int status = i2c_smbus_read_word_swapped(client,
tmp102_reg[i]);
if (status > -1)
tmp102->temp[i] = tmp102_reg_to_mC(status);
}
tmp102->last_update = jiffies;
tmp102->first_time = false;
}
mutex_unlock(&tmp102->lock);
return tmp102;
}
static int tmp102_read_temp(void *dev, int *temp)
{
struct tmp102 *tmp102 = tmp102_update_device(dev);
struct tmp102 *tmp102 = dev_get_drvdata(dev);
unsigned int reg;
int ret;
/* Is it too early even to return a conversion? */
if (tmp102->first_time) {
if (time_before(jiffies, tmp102->ready_time)) {
dev_dbg(dev, "%s: Conversion not ready yet..\n", __func__);
return -EAGAIN;
}
*temp = tmp102->temp[0];
ret = regmap_read(tmp102->regmap, TMP102_TEMP_REG, &reg);
if (ret < 0)
return ret;
*temp = tmp102_reg_to_mC(reg);
return 0;
}
@ -119,13 +104,20 @@ 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(dev);
struct tmp102 *tmp102 = dev_get_drvdata(dev);
int regaddr = sda->index;
unsigned int reg;
int err;
/* Is it too early even to return a read? */
if (tmp102->first_time)
if (regaddr == TMP102_TEMP_REG &&
time_before(jiffies, tmp102->ready_time))
return -EAGAIN;
return sprintf(buf, "%d\n", tmp102->temp[sda->index]);
err = regmap_read(tmp102->regmap, regaddr, &reg);
if (err < 0)
return err;
return sprintf(buf, "%d\n", tmp102_reg_to_mC(reg));
}
static ssize_t tmp102_set_temp(struct device *dev,
@ -134,29 +126,26 @@ static ssize_t tmp102_set_temp(struct device *dev,
{
struct sensor_device_attribute *sda = to_sensor_dev_attr(attr);
struct tmp102 *tmp102 = dev_get_drvdata(dev);
struct i2c_client *client = tmp102->client;
int reg = sda->index;
long val;
int status;
int err;
if (kstrtol(buf, 10, &val) < 0)
return -EINVAL;
val = clamp_val(val, -256000, 255000);
mutex_lock(&tmp102->lock);
tmp102->temp[sda->index] = val;
status = i2c_smbus_write_word_swapped(client, tmp102_reg[sda->index],
tmp102_mC_to_reg(val));
mutex_unlock(&tmp102->lock);
return status ? : count;
err = regmap_write(tmp102->regmap, reg, tmp102_mC_to_reg(val));
return err ? : count;
}
static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, tmp102_show_temp, NULL , 0);
static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, tmp102_show_temp, NULL,
TMP102_TEMP_REG);
static SENSOR_DEVICE_ATTR(temp1_max_hyst, S_IWUSR | S_IRUGO, tmp102_show_temp,
tmp102_set_temp, 1);
tmp102_set_temp, TMP102_TLOW_REG);
static SENSOR_DEVICE_ATTR(temp1_max, S_IWUSR | S_IRUGO, tmp102_show_temp,
tmp102_set_temp, 2);
tmp102_set_temp, TMP102_THIGH_REG);
static struct attribute *tmp102_attrs[] = {
&sensor_dev_attr_temp1_input.dev_attr.attr,
@ -166,20 +155,46 @@ static struct attribute *tmp102_attrs[] = {
};
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)
static const struct thermal_zone_of_device_ops tmp102_of_thermal_ops = {
.get_temp = tmp102_read_temp,
};
static void tmp102_restore_config(void *data)
{
struct tmp102 *tmp102 = data;
regmap_write(tmp102->regmap, TMP102_CONF_REG, tmp102->config_orig);
}
static bool tmp102_is_writeable_reg(struct device *dev, unsigned int reg)
{
return reg != TMP102_TEMP_REG;
}
static bool tmp102_is_volatile_reg(struct device *dev, unsigned int reg)
{
return reg == TMP102_TEMP_REG;
}
static const struct regmap_config tmp102_regmap_config = {
.reg_bits = 8,
.val_bits = 16,
.max_register = TMP102_THIGH_REG,
.writeable_reg = tmp102_is_writeable_reg,
.volatile_reg = tmp102_is_volatile_reg,
.val_format_endian = REGMAP_ENDIAN_BIG,
.cache_type = REGCACHE_RBTREE,
.use_single_rw = true,
};
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;
unsigned int regval;
int err;
if (!i2c_check_functionality(client->adapter,
I2C_FUNC_SMBUS_WORD_DATA)) {
@ -193,73 +208,57 @@ static int tmp102_probe(struct i2c_client *client,
return -ENOMEM;
i2c_set_clientdata(client, tmp102);
tmp102->client = client;
status = i2c_smbus_read_word_swapped(client, TMP102_CONF_REG);
if (status < 0) {
tmp102->regmap = devm_regmap_init_i2c(client, &tmp102_regmap_config);
if (IS_ERR(tmp102->regmap))
return PTR_ERR(tmp102->regmap);
err = regmap_read(tmp102->regmap, TMP102_CONF_REG, &regval);
if (err < 0) {
dev_err(dev, "error reading config register\n");
return status;
return err;
}
tmp102->config_orig = status;
status = i2c_smbus_write_word_swapped(client, TMP102_CONF_REG,
TMP102_CONFIG);
if (status < 0) {
if ((regval & ~TMP102_CONFREG_MASK) !=
(TMP102_CONF_R0 | TMP102_CONF_R1)) {
dev_err(dev, "unexpected config register value\n");
return -ENODEV;
}
tmp102->config_orig = regval;
devm_add_action(dev, tmp102_restore_config, tmp102);
regval &= ~TMP102_CONFIG_CLEAR;
regval |= TMP102_CONFIG_SET;
err = regmap_write(tmp102->regmap, TMP102_CONF_REG, regval);
if (err < 0) {
dev_err(dev, "error writing config register\n");
goto fail_restore_config;
return err;
}
status = i2c_smbus_read_word_swapped(client, TMP102_CONF_REG);
if (status < 0) {
dev_err(dev, "error reading config register\n");
goto fail_restore_config;
}
status &= ~TMP102_CONFIG_RD_ONLY;
if (status != TMP102_CONFIG) {
dev_err(dev, "config settings did not stick\n");
status = -ENODEV;
goto fail_restore_config;
}
tmp102->last_update = jiffies;
/* Mark that we are not ready with data until conversion is complete */
tmp102->first_time = true;
mutex_init(&tmp102->lock);
hwmon_dev = hwmon_device_register_with_groups(dev, client->name,
tmp102, tmp102_groups);
tmp102->ready_time = jiffies;
if (tmp102->config_orig & TMP102_CONF_SD) {
/*
* Mark that we are not ready with data until the first
* conversion is complete
*/
tmp102->ready_time += msecs_to_jiffies(CONVERSION_TIME_MS);
}
hwmon_dev = devm_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;
return PTR_ERR(hwmon_dev);
}
tmp102->hwmon_dev = hwmon_dev;
devm_thermal_zone_of_sensor_register(hwmon_dev, 0, hwmon_dev,
&tmp102_of_thermal_ops);
dev_info(dev, "initialized\n");
return 0;
fail_restore_config:
i2c_smbus_write_word_swapped(client, TMP102_CONF_REG,
tmp102->config_orig);
return status;
}
static int tmp102_remove(struct i2c_client *client)
{
struct tmp102 *tmp102 = i2c_get_clientdata(client);
hwmon_device_unregister(tmp102->hwmon_dev);
/* Stop monitoring if device was stopped originally */
if (tmp102->config_orig & TMP102_CONF_SD) {
int config;
config = i2c_smbus_read_word_swapped(client, TMP102_CONF_REG);
if (config >= 0)
i2c_smbus_write_word_swapped(client, TMP102_CONF_REG,
config | TMP102_CONF_SD);
}
return 0;
}
@ -267,27 +266,24 @@ static int tmp102_remove(struct i2c_client *client)
static int tmp102_suspend(struct device *dev)
{
struct i2c_client *client = to_i2c_client(dev);
int config;
struct tmp102 *tmp102 = i2c_get_clientdata(client);
config = i2c_smbus_read_word_swapped(client, TMP102_CONF_REG);
if (config < 0)
return config;
config |= TMP102_CONF_SD;
return i2c_smbus_write_word_swapped(client, TMP102_CONF_REG, config);
return regmap_update_bits(tmp102->regmap, TMP102_CONF_REG,
TMP102_CONF_SD, TMP102_CONF_SD);
}
static int tmp102_resume(struct device *dev)
{
struct i2c_client *client = to_i2c_client(dev);
int config;
struct tmp102 *tmp102 = i2c_get_clientdata(client);
int err;
config = i2c_smbus_read_word_swapped(client, TMP102_CONF_REG);
if (config < 0)
return config;
err = regmap_update_bits(tmp102->regmap, TMP102_CONF_REG,
TMP102_CONF_SD, 0);
config &= ~TMP102_CONF_SD;
return i2c_smbus_write_word_swapped(client, TMP102_CONF_REG, config);
tmp102->ready_time = jiffies + msecs_to_jiffies(CONVERSION_TIME_MS);
return err;
}
#endif /* CONFIG_PM */
@ -303,7 +299,6 @@ static struct i2c_driver tmp102_driver = {
.driver.name = DRIVER_NAME,
.driver.pm = &tmp102_dev_pm_ops,
.probe = tmp102_probe,
.remove = tmp102_remove,
.id_table = tmp102_id,
};

View File

@ -47,7 +47,7 @@
static const unsigned short normal_i2c[] = { 0x48, 0x49, 0x4a, 0x4c, 0x4d,
0x4e, 0x4f, I2C_CLIENT_END };
enum chips { tmp401, tmp411, tmp431, tmp432, tmp435 };
enum chips { tmp401, tmp411, tmp431, tmp432, tmp435, tmp461 };
/*
* The TMP401 registers, note some registers have different addresses for
@ -62,31 +62,34 @@ enum chips { tmp401, tmp411, tmp431, tmp432, tmp435 };
#define TMP401_MANUFACTURER_ID_REG 0xFE
#define TMP401_DEVICE_ID_REG 0xFF
static const u8 TMP401_TEMP_MSB_READ[6][2] = {
static const u8 TMP401_TEMP_MSB_READ[7][2] = {
{ 0x00, 0x01 }, /* temp */
{ 0x06, 0x08 }, /* low limit */
{ 0x05, 0x07 }, /* high limit */
{ 0x20, 0x19 }, /* therm (crit) limit */
{ 0x30, 0x34 }, /* lowest */
{ 0x32, 0x36 }, /* highest */
{ 0, 0x11 }, /* offset */
};
static const u8 TMP401_TEMP_MSB_WRITE[6][2] = {
static const u8 TMP401_TEMP_MSB_WRITE[7][2] = {
{ 0, 0 }, /* temp (unused) */
{ 0x0C, 0x0E }, /* low limit */
{ 0x0B, 0x0D }, /* high limit */
{ 0x20, 0x19 }, /* therm (crit) limit */
{ 0x30, 0x34 }, /* lowest */
{ 0x32, 0x36 }, /* highest */
{ 0, 0x11 }, /* offset */
};
static const u8 TMP401_TEMP_LSB[6][2] = {
static const u8 TMP401_TEMP_LSB[7][2] = {
{ 0x15, 0x10 }, /* temp */
{ 0x17, 0x14 }, /* low limit */
{ 0x16, 0x13 }, /* high limit */
{ 0, 0 }, /* therm (crit) limit (unused) */
{ 0x31, 0x35 }, /* lowest */
{ 0x33, 0x37 }, /* highest */
{ 0, 0x12 }, /* offset */
};
static const u8 TMP432_TEMP_MSB_READ[4][3] = {
@ -149,6 +152,7 @@ static const struct i2c_device_id tmp401_id[] = {
{ "tmp431", tmp431 },
{ "tmp432", tmp432 },
{ "tmp435", tmp435 },
{ "tmp461", tmp461 },
{ }
};
MODULE_DEVICE_TABLE(i2c, tmp401_id);
@ -170,7 +174,7 @@ struct tmp401_data {
/* register values */
u8 status[4];
u8 config;
u16 temp[6][3];
u16 temp[7][3];
u8 temp_crit_hyst;
};
@ -612,6 +616,22 @@ static const struct attribute_group tmp432_group = {
.attrs = tmp432_attributes,
};
/*
* Additional features of the TMP461 chip.
* The TMP461 temperature offset for the remote channel.
*/
static SENSOR_DEVICE_ATTR_2(temp2_offset, S_IWUSR | S_IRUGO, show_temp,
store_temp, 6, 1);
static struct attribute *tmp461_attributes[] = {
&sensor_dev_attr_temp2_offset.dev_attr.attr,
NULL
};
static const struct attribute_group tmp461_group = {
.attrs = tmp461_attributes,
};
/*
* Begin non sysfs callback code (aka Real code)
*/
@ -714,7 +734,7 @@ static int tmp401_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
static const char * const names[] = {
"TMP401", "TMP411", "TMP431", "TMP432", "TMP435"
"TMP401", "TMP411", "TMP431", "TMP432", "TMP435", "TMP461"
};
struct device *dev = &client->dev;
struct device *hwmon_dev;
@ -745,6 +765,9 @@ static int tmp401_probe(struct i2c_client *client,
if (data->kind == tmp432)
data->groups[groups++] = &tmp432_group;
if (data->kind == tmp461)
data->groups[groups++] = &tmp461_group;
hwmon_dev = devm_hwmon_device_register_with_groups(dev, client->name,
data, data->groups);
if (IS_ERR(hwmon_dev))

View File

@ -0,0 +1,25 @@
/*
* Copyright (C) 2016 Sensirion AG, Switzerland
* Author: David Frey <david.frey@sensirion.com>
* Author: Pascal Sachs <pascal.sachs@sensirion.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*/
#ifndef __SHT3X_H_
#define __SHT3X_H_
struct sht3x_platform_data {
bool blocking_io;
bool high_precision;
};
#endif /* __SHT3X_H_ */