hwmon updates for v4.5:

- New drivers for NSA320 and LTC2990
 - Added support for ADM1278 to adm1275 driver
 - Added support for ncpXXxh103 to ntc_thermistor driver
 - Renamed vexpress hwmon implementation
 - Minor cleanups and improvements
 -----BEGIN PGP SIGNATURE-----
 Version: GnuPG v1
 
 iQIcBAABAgAGBQJW5sANAAoJEMsfJm/On5mBKTEP/2SIXOFN+ew3Lhmz36Z8gDJY
 LpVcFjb8S/nRDibvaYjaO5r7p6x2d3jW6wkdpt1V44l5khvODjnaZhH7qZ1xaVuI
 qt9xRX0PvHAlwIpXHRNgueNAkAwTuCJqmbbYGrPQJCe4zu0sxiDrpRa117Ym1AJY
 NxOts2n/VAjVn0O+F4QiJELDmSpajsVt7QPjUGYfiEHPaGDgPVq+5gqRPeL9hfOu
 tjFVWn3YVJTI/Q3N/aB0+XQpwoZdu74RFnr/x7rkjzVcqiNdq0+WtFAIAS9Aw3s1
 gZbwRPX7Pu/sNmcVRaj+9rSpzl9uzHbqD4+/+hCuv3LjnyAbLm3NmV2EB1u42SwE
 bmqjnCTtC5InlkcKGi85Sv72YcGj5MgddSTQ9TDBKQIW8Z70rnFK/7xPbq776W/L
 vFXQ62YZ14lX3sVTMVfntf6BrYeteVbJmP7d9SXeTHfHVSSaxwGrq6kim8m2zi/J
 R8YMsVOok0P3ldyua73TIdujYtpz2AZ4vdddDFSlu/m1o/bp642le5HLVuKBpuYy
 Szjc26JGL5ymGLAUmeEUt8jOWUqQo9MLw9Gc8kgc/Zui3q+lMJKXZ6dvkgJpnFCL
 xw/fB/E6x4pDEhJPe3LeFcKBk/vF6bktEWrrA0rGHrVGJIaOhgPkxyeGV7BdNqFS
 DtMy4EcjmlAKOn5oxOL5
 =n07O
 -----END PGP SIGNATURE-----

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

Pull hwmon updates from Guenter Roeck:
 - New drivers for NSA320 and LTC2990
 - Added support for ADM1278 to adm1275 driver
 - Added support for ncpXXxh103 to ntc_thermistor driver
 - Renamed vexpress hwmon implementation
 - Minor cleanups and improvements

* tag 'hwmon-for-linus-v4.6' of git://git.kernel.org/pub/scm/linux/kernel/git/groeck/linux-staging:
  hwmon: Create an NSA320 hardware monitoring driver
  hwmon: Define binding for the nsa320-hwmon driver
  hwmon: (adm1275) Add support for ADM1278
  hwmon: (ntc_thermistor) Add support for ncpXXxh103
  Doc: hwmon: Fix typo "montoring" in hwmon
  ARM: dts: vfxxx: Add iio_hwmon node for ADC temperature channel
  ARM: dts: Change iio_hwmon nodes to use hypen in node names
  hwmon: (iio_hwmon) Allow the driver to accept hypen in device tree node names
  hwmon: Add LTC2990 sensor driver
  hwmon: (vexpress) rename vexpress hwmon implementation
This commit is contained in:
Linus Torvalds 2016-03-15 21:44:47 -07:00
commit f0718cea47
26 changed files with 677 additions and 23 deletions

View File

@ -0,0 +1,20 @@
Bindings for the fan / temperature monitor microcontroller used on
the Zyxel NSA 320 and several subsequent models.
Required properties:
- compatible : "zyxel,nsa320-mcu"
- data-gpios : The GPIO pin connected to the data line on the MCU
- clk-gpios : The GPIO pin connected to the clock line on the MCU
- act-gpios : The GPIO pin connected to the active line on the MCU
Example:
hwmon {
compatible = "zyxel,nsa320-mcu";
pinctrl-0 = <&pmx_mcu_data &pmx_mcu_clk &pmx_mcu_act>;
pinctrl-names = "default";
data-gpios = <&gpio0 14 GPIO_ACTIVE_HIGH>;
clk-gpios = <&gpio0 16 GPIO_ACTIVE_HIGH>;
act-gpios = <&gpio0 17 GPIO_ACTIVE_LOW>;
};

View File

@ -10,6 +10,7 @@ Requires node properties:
"murata,ncp03wb473"
"murata,ncp15wl333"
"murata,ncp03wf104"
"murata,ncp15xh103"
/* Usage of vendor name "ntc" is deprecated */
<DEPRECATED> "ntc,ncp15wb473"

View File

@ -82,7 +82,7 @@ vdd channel is connected to output 0 of the &ref device.
...
iio_hwmon {
iio-hwmon {
compatible = "iio-hwmon";
io-channels = <&adc 0>, <&adc 1>, <&adc 2>,
<&adc 3>, <&adc 4>, <&adc 5>,

View File

@ -14,6 +14,10 @@ Supported chips:
Prefix: 'adm1276'
Addresses scanned: -
Datasheet: www.analog.com/static/imported-files/data_sheets/ADM1276.pdf
* Analog Devices ADM1278
Prefix: 'adm1278'
Addresses scanned: -
Datasheet: www.analog.com/static/imported-files/data_sheets/ADM1278.pdf
* Analog Devices ADM1293/ADM1294
Prefix: 'adm1293', 'adm1294'
Addresses scanned: -
@ -25,13 +29,15 @@ Author: Guenter Roeck <linux@roeck-us.net>
Description
-----------
This driver supports hardware montoring for Analog Devices ADM1075, ADM1275,
ADM1276, ADM1293, and ADM1294 Hot-Swap Controller and Digital Power Monitors.
This driver supports hardware monitoring for Analog Devices ADM1075, ADM1275,
ADM1276, ADM1278, ADM1293, and ADM1294 Hot-Swap Controller and Digital
Power Monitors.
ADM1075, ADM1275, ADM1276, ADM1293, and ADM1294 are hot-swap controllers that
allow a circuit board to be removed from or inserted into a live backplane.
They also feature current and voltage readback via an integrated 12
bit analog-to-digital converter (ADC), accessed using a PMBus interface.
ADM1075, ADM1275, ADM1276, ADM1278, ADM1293, and ADM1294 are hot-swap
controllers that allow a circuit board to be removed from or inserted into
a live backplane. They also feature current and voltage readback via an
integrated 12 bit analog-to-digital converter (ADC), accessed using a
PMBus interface.
The driver is a client driver to the core PMBus driver. Please see
Documentation/hwmon/pmbus for details on PMBus client drivers.
@ -96,3 +102,14 @@ power1_reset_history Write any value to reset history.
Power attributes are supported on ADM1075, ADM1276,
ADM1293, and ADM1294.
temp1_input Chip temperature.
Temperature attributes are only available on ADM1278.
temp1_max Maximum chip temperature.
temp1_max_alarm Temperature alarm.
temp1_crit Critical chip temperature.
temp1_crit_alarm Critical temperature high alarm.
temp1_highest Highest observed temperature.
temp1_reset_history Write any value to reset history.
Temperature attributes are supported on ADM1278.

View File

@ -36,7 +36,7 @@ Author: Guenter Roeck <linux@roeck-us.net>
Description
-----------
This driver supports hardware montoring for National Semiconductor / TI LM25056,
This driver supports hardware monitoring for National Semiconductor / TI LM25056,
LM25063, LM25066, LM5064, and LM5066 Power Management, Monitoring, Control, and
Protection ICs.

View File

@ -0,0 +1,43 @@
Kernel driver ltc2990
=====================
Supported chips:
* Linear Technology LTC2990
Prefix: 'ltc2990'
Addresses scanned: -
Datasheet: http://www.linear.com/product/ltc2990
Author: Mike Looijmans <mike.looijmans@topic.nl>
Description
-----------
LTC2990 is a Quad I2C Voltage, Current and Temperature Monitor.
The chip's inputs can measure 4 voltages, or two inputs together (1+2 and 3+4)
can be combined to measure a differential voltage, which is typically used to
measure current through a series resistor, or a temperature.
This driver currently uses the 2x differential mode only. In order to support
other modes, the driver will need to be expanded.
Usage Notes
-----------
This driver does not probe for PMBus devices. You will have to instantiate
devices explicitly.
Sysfs attributes
----------------
The "curr*_input" measurements actually report the voltage drop across the
input pins in microvolts. This is equivalent to the current through a 1mOhm
sense resistor. Divide the reported value by the actual sense resistor value
in mOhm to get the actual value.
in0_input Voltage at Vcc pin in millivolt (range 2.5V to 5V)
temp1_input Internal chip temperature in millidegrees Celcius
curr1_input Current in mA across v1-v2 assuming a 1mOhm sense resistor.
curr2_input Current in mA across v3-v4 assuming a 1mOhm sense resistor.

View File

@ -13,7 +13,7 @@ Author: Guenter Roeck <linux@roeck-us.net>
Description
-----------
This driver supports hardware montoring for Maxim MAX16064 Quad Power-Supply
This driver supports hardware monitoring for Maxim MAX16064 Quad Power-Supply
Controller with Active-Voltage Output Control and PMBus Interface.
The driver is a client driver to the core PMBus driver.

View File

@ -33,7 +33,7 @@ Author: Guenter Roeck <linux@roeck-us.net>
Description
-----------
This driver supports hardware montoring for Maxim MAX34440 PMBus 6-Channel
This driver supports hardware monitoring for Maxim MAX34440 PMBus 6-Channel
Power-Supply Manager, MAX34441 PMBus 5-Channel Power-Supply Manager
and Intelligent Fan Controller, and MAX34446 PMBus Power-Supply Data Logger.
It also supports the MAX34460 and MAX34461 PMBus Voltage Monitor & Sequencers.

View File

@ -13,7 +13,7 @@ Author: Guenter Roeck <linux@roeck-us.net>
Description
-----------
This driver supports hardware montoring for Maxim MAX8688 Digital Power-Supply
This driver supports hardware monitoring for Maxim MAX8688 Digital Power-Supply
Controller/Monitor with PMBus Interface.
The driver is a client driver to the core PMBus driver. Please see

View File

@ -0,0 +1,53 @@
Kernel driver nsa320_hwmon
==========================
Supported chips:
* Holtek HT46R065 microcontroller with onboard firmware that configures
it to act as a hardware monitor.
Prefix: 'nsa320'
Addresses scanned: none
Datasheet: Not available, driver was reverse engineered based upon the
Zyxel kernel source
Author:
Adam Baker <linux@baker-net.org.uk>
Description
-----------
This chip is known to be used in the Zyxel NSA320 and NSA325 NAS Units and
also in some variants of the NSA310 but the driver has only been tested
on the NSA320. In all of these devices it is connected to the same 3 GPIO
lines which are used to provide chip select, clock and data lines. The
interface behaves similarly to SPI but at much lower speeds than are normally
used for SPI.
Following each chip select pulse the chip will generate a single 32 bit word
that contains 0x55 as a marker to indicate that data is being read correctly,
followed by an 8 bit fan speed in 100s of RPM and a 16 bit temperature in
tenths of a degree.
sysfs-Interface
---------------
temp1_input - temperature input
fan1_input - fan speed
Notes
-----
The access timings used in the driver are the same as used in the Zyxel
provided kernel. Testing has shown that if the delay between chip select and
the first clock pulse is reduced from 100 ms to just under 10ms then the chip
will not produce any output. If the duration of either phase of the clock
is reduced from 100 us to less than 15 us then data pulses are likely to be
read twice corrupting the output. The above analysis is based upon a sample
of one unit but suggests that the Zyxel provided delay values include a
reasonable tolerance.
The driver incorporates a limit that it will not check for updated values
faster than once a second. This is because the hardware takes a relatively long
time to read the data from the device and when it does it reads both temp and
fan speed. As the most likely case for two accesses in quick succession is
to read both of these values avoiding a second read delay is desirable.

View File

@ -3,9 +3,9 @@ Kernel driver ntc_thermistor
Supported thermistors from Murata:
* Murata NTC Thermistors NCP15WB473, NCP18WB473, NCP21WB473, NCP03WB473,
NCP15WL333, NCP03WF104
NCP15WL333, NCP03WF104, NCP15XH103
Prefixes: 'ncp15wb473', 'ncp18wb473', 'ncp21wb473', 'ncp03wb473',
'ncp15wl333', 'ncp03wf104'
'ncp15wl333', 'ncp03wf104', 'ncp15xh103'
Datasheet: Publicly available at Murata
Supported thermistors from EPCOS:

View File

@ -43,7 +43,7 @@ Author: Guenter Roeck <linux@roeck-us.net>
Description
-----------
This driver supports hardware montoring for various PMBus compliant devices.
This driver supports hardware monitoring for various PMBus compliant devices.
It supports voltage, current, power, and temperature sensors as supported
by the device.

View File

@ -60,7 +60,7 @@ Author: Guenter Roeck <linux@roeck-us.net>
Description
-----------
This driver supports hardware montoring for Intersil / Zilker Labs ZL6100 and
This driver supports hardware monitoring for Intersil / Zilker Labs ZL6100 and
compatible digital DC-DC controllers.
The driver is a client driver to the core PMBus driver. Please see

View File

@ -569,7 +569,7 @@ usb0: usb@80080000 {
};
};
iio_hwmon {
iio-hwmon {
compatible = "iio-hwmon";
io-channels = <&lradc 8>;
};

View File

@ -1256,7 +1256,7 @@ etn_switch: switch@800f8000 {
};
};
iio_hwmon {
iio-hwmon {
compatible = "iio-hwmon";
io-channels = <&lradc 8>;
};

View File

@ -629,5 +629,10 @@ i2c3: i2c@400e7000 {
status = "disabled";
};
};
iio-hwmon {
compatible = "iio-hwmon";
io-channels = <&adc0 16>, <&adc1 16>;
};
};
};

View File

@ -685,6 +685,20 @@ config SENSORS_LTC2945
This driver can also be built as a module. If so, the module will
be called ltc2945.
config SENSORS_LTC2990
tristate "Linear Technology LTC2990 (current monitoring mode only)"
depends on I2C
help
If you say yes here you get support for Linear Technology LTC2990
I2C System Monitor. The LTC2990 supports a combination of voltage,
current and temperature monitoring, but in addition to the Vcc supply
voltage and chip temperature, this driver currently only supports
reading two currents by measuring two differential voltages across
series resistors.
This driver can also be built as a module. If so, the module will
be called ltc2990.
config SENSORS_LTC4151
tristate "Linear Technology LTC4151"
depends on I2C
@ -1127,7 +1141,7 @@ config SENSORS_NTC_THERMISTOR
Currently, this driver supports
NCP15WB473, NCP18WB473, NCP21WB473, NCP03WB473, NCP15WL333,
and NCP03WF104 from Murata and B57330V2103 from EPCOS.
NCP03WF104 and NCP15XH103 from Murata and B57330V2103 from EPCOS.
This driver can also be built as a module. If so, the module
will be called ntc-thermistor.
@ -1176,6 +1190,21 @@ config SENSORS_NCT7904
This driver can also be built as a module. If so, the module
will be called nct7904.
config SENSORS_NSA320
tristate "ZyXEL NSA320 and compatible fan speed and temperature sensors"
depends on GPIOLIB && OF
depends on MACH_KIRKWOOD || COMPILE_TEST
help
If you say yes here you get support for hardware monitoring
for the ZyXEL NSA320 Media Server and other compatible devices
(probably the NSA325 and some NSA310 variants).
The sensor data is taken from a Holtek HT46R065 microcontroller
connected to GPIO lines.
This driver can also be built as a module. If so, the module
will be called nsa320-hwmon.
config SENSORS_PCF8591
tristate "Philips PCF8591 ADC/DAC"
depends on I2C

View File

@ -100,6 +100,7 @@ obj-$(CONFIG_SENSORS_LM95234) += lm95234.o
obj-$(CONFIG_SENSORS_LM95241) += lm95241.o
obj-$(CONFIG_SENSORS_LM95245) += lm95245.o
obj-$(CONFIG_SENSORS_LTC2945) += ltc2945.o
obj-$(CONFIG_SENSORS_LTC2990) += ltc2990.o
obj-$(CONFIG_SENSORS_LTC4151) += ltc4151.o
obj-$(CONFIG_SENSORS_LTC4215) += ltc4215.o
obj-$(CONFIG_SENSORS_LTC4222) += ltc4222.o
@ -123,6 +124,7 @@ obj-$(CONFIG_SENSORS_NCT6683) += nct6683.o
obj-$(CONFIG_SENSORS_NCT6775) += nct6775.o
obj-$(CONFIG_SENSORS_NCT7802) += nct7802.o
obj-$(CONFIG_SENSORS_NCT7904) += nct7904.o
obj-$(CONFIG_SENSORS_NSA320) += nsa320-hwmon.o
obj-$(CONFIG_SENSORS_NTC_THERMISTOR) += ntc_thermistor.o
obj-$(CONFIG_SENSORS_PC87360) += pc87360.o
obj-$(CONFIG_SENSORS_PC87427) += pc87427.o
@ -149,7 +151,7 @@ obj-$(CONFIG_SENSORS_TMP103) += tmp103.o
obj-$(CONFIG_SENSORS_TMP401) += tmp401.o
obj-$(CONFIG_SENSORS_TMP421) += tmp421.o
obj-$(CONFIG_SENSORS_TWL4030_MADC)+= twl4030-madc-hwmon.o
obj-$(CONFIG_SENSORS_VEXPRESS) += vexpress.o
obj-$(CONFIG_SENSORS_VEXPRESS) += vexpress-hwmon.o
obj-$(CONFIG_SENSORS_VIA_CPUTEMP)+= via-cputemp.o
obj-$(CONFIG_SENSORS_VIA686A) += via686a.o
obj-$(CONFIG_SENSORS_VT1211) += vt1211.o

View File

@ -67,6 +67,7 @@ static int iio_hwmon_probe(struct platform_device *pdev)
enum iio_chan_type type;
struct iio_channel *channels;
const char *name = "iio_hwmon";
char *sname;
if (dev->of_node && dev->of_node->name)
name = dev->of_node->name;
@ -144,7 +145,15 @@ static int iio_hwmon_probe(struct platform_device *pdev)
st->attr_group.attrs = st->attrs;
st->groups[0] = &st->attr_group;
st->hwmon_dev = hwmon_device_register_with_groups(dev, name, st,
sname = devm_kstrdup(dev, name, GFP_KERNEL);
if (!sname) {
ret = -ENOMEM;
goto error_release_channels;
}
strreplace(sname, '-', '_');
st->hwmon_dev = hwmon_device_register_with_groups(dev, sname, st,
st->groups);
if (IS_ERR(st->hwmon_dev)) {
ret = PTR_ERR(st->hwmon_dev);

161
drivers/hwmon/ltc2990.c Normal file
View File

@ -0,0 +1,161 @@
/*
* Driver for Linear Technology LTC2990 power monitor
*
* Copyright (C) 2014 Topic Embedded Products
* Author: Mike Looijmans <mike.looijmans@topic.nl>
*
* License: GPLv2
*
* This driver assumes the chip is wired as a dual current monitor, and
* reports the voltage drop across two series resistors. It also reports
* the chip's internal temperature and Vcc power supply voltage.
*/
#include <linux/err.h>
#include <linux/hwmon.h>
#include <linux/hwmon-sysfs.h>
#include <linux/i2c.h>
#include <linux/kernel.h>
#include <linux/module.h>
#define LTC2990_STATUS 0x00
#define LTC2990_CONTROL 0x01
#define LTC2990_TRIGGER 0x02
#define LTC2990_TINT_MSB 0x04
#define LTC2990_V1_MSB 0x06
#define LTC2990_V2_MSB 0x08
#define LTC2990_V3_MSB 0x0A
#define LTC2990_V4_MSB 0x0C
#define LTC2990_VCC_MSB 0x0E
#define LTC2990_CONTROL_KELVIN BIT(7)
#define LTC2990_CONTROL_SINGLE BIT(6)
#define LTC2990_CONTROL_MEASURE_ALL (0x3 << 3)
#define LTC2990_CONTROL_MODE_CURRENT 0x06
#define LTC2990_CONTROL_MODE_VOLTAGE 0x07
/* convert raw register value to sign-extended integer in 16-bit range */
static int ltc2990_voltage_to_int(int raw)
{
if (raw & BIT(14))
return -(0x4000 - (raw & 0x3FFF)) << 2;
else
return (raw & 0x3FFF) << 2;
}
/* Return the converted value from the given register in uV or mC */
static int ltc2990_get_value(struct i2c_client *i2c, u8 reg, int *result)
{
int val;
val = i2c_smbus_read_word_swapped(i2c, reg);
if (unlikely(val < 0))
return val;
switch (reg) {
case LTC2990_TINT_MSB:
/* internal temp, 0.0625 degrees/LSB, 13-bit */
val = (val & 0x1FFF) << 3;
*result = (val * 1000) >> 7;
break;
case LTC2990_V1_MSB:
case LTC2990_V3_MSB:
/* Vx-Vy, 19.42uV/LSB. Depends on mode. */
*result = ltc2990_voltage_to_int(val) * 1942 / (4 * 100);
break;
case LTC2990_VCC_MSB:
/* Vcc, 305.18μV/LSB, 2.5V offset */
*result = (ltc2990_voltage_to_int(val) * 30518 /
(4 * 100 * 1000)) + 2500;
break;
default:
return -EINVAL; /* won't happen, keep compiler happy */
}
return 0;
}
static ssize_t ltc2990_show_value(struct device *dev,
struct device_attribute *da, char *buf)
{
struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
int value;
int ret;
ret = ltc2990_get_value(dev_get_drvdata(dev), attr->index, &value);
if (unlikely(ret < 0))
return ret;
return snprintf(buf, PAGE_SIZE, "%d\n", value);
}
static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, ltc2990_show_value, NULL,
LTC2990_TINT_MSB);
static SENSOR_DEVICE_ATTR(curr1_input, S_IRUGO, ltc2990_show_value, NULL,
LTC2990_V1_MSB);
static SENSOR_DEVICE_ATTR(curr2_input, S_IRUGO, ltc2990_show_value, NULL,
LTC2990_V3_MSB);
static SENSOR_DEVICE_ATTR(in0_input, S_IRUGO, ltc2990_show_value, NULL,
LTC2990_VCC_MSB);
static struct attribute *ltc2990_attrs[] = {
&sensor_dev_attr_temp1_input.dev_attr.attr,
&sensor_dev_attr_curr1_input.dev_attr.attr,
&sensor_dev_attr_curr2_input.dev_attr.attr,
&sensor_dev_attr_in0_input.dev_attr.attr,
NULL,
};
ATTRIBUTE_GROUPS(ltc2990);
static int ltc2990_i2c_probe(struct i2c_client *i2c,
const struct i2c_device_id *id)
{
int ret;
struct device *hwmon_dev;
if (!i2c_check_functionality(i2c->adapter, I2C_FUNC_SMBUS_BYTE_DATA |
I2C_FUNC_SMBUS_WORD_DATA))
return -ENODEV;
/* Setup continuous mode, current monitor */
ret = i2c_smbus_write_byte_data(i2c, LTC2990_CONTROL,
LTC2990_CONTROL_MEASURE_ALL |
LTC2990_CONTROL_MODE_CURRENT);
if (ret < 0) {
dev_err(&i2c->dev, "Error: Failed to set control mode.\n");
return ret;
}
/* Trigger once to start continuous conversion */
ret = i2c_smbus_write_byte_data(i2c, LTC2990_TRIGGER, 1);
if (ret < 0) {
dev_err(&i2c->dev, "Error: Failed to start acquisition.\n");
return ret;
}
hwmon_dev = devm_hwmon_device_register_with_groups(&i2c->dev,
i2c->name,
i2c,
ltc2990_groups);
return PTR_ERR_OR_ZERO(hwmon_dev);
}
static const struct i2c_device_id ltc2990_i2c_id[] = {
{ "ltc2990", 0 },
{}
};
MODULE_DEVICE_TABLE(i2c, ltc2990_i2c_id);
static struct i2c_driver ltc2990_i2c_driver = {
.driver = {
.name = "ltc2990",
},
.probe = ltc2990_i2c_probe,
.id_table = ltc2990_i2c_id,
};
module_i2c_driver(ltc2990_i2c_driver);
MODULE_DESCRIPTION("LTC2990 Sensor Driver");
MODULE_AUTHOR("Topic Embedded Products");
MODULE_LICENSE("GPL v2");

View File

@ -0,0 +1,215 @@
/*
* drivers/hwmon/nsa320-hwmon.c
*
* ZyXEL NSA320 Media Servers
* hardware monitoring
*
* Copyright (C) 2016 Adam Baker <linux@baker-net.org.uk>
* based on a board file driver
* Copyright (C) 2012 Peter Schildmann <linux@schildmann.info>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License v2 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/bitops.h>
#include <linux/delay.h>
#include <linux/err.h>
#include <linux/gpio/consumer.h>
#include <linux/hwmon.h>
#include <linux/hwmon-sysfs.h>
#include <linux/jiffies.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/of_platform.h>
#include <linux/platform_device.h>
/* Tests for error return values rely upon this value being < 0x80 */
#define MAGIC_NUMBER 0x55
/*
* The Zyxel hwmon MCU is a Holtek HT46R065 that is factory programmed
* to perform temperature and fan speed monitoring. It is read by taking
* the active pin low. The 32 bit output word is then clocked onto the
* data line. The MSB of the data word is a magic nuber to indicate it
* has been read correctly, the next byte is the fan speed (in hundreds
* of RPM) and the last two bytes are the temperature (in tenths of a
* degree)
*/
struct nsa320_hwmon {
struct mutex update_lock; /* lock GPIO operations */
unsigned long last_updated; /* jiffies */
unsigned long mcu_data;
struct gpio_desc *act;
struct gpio_desc *clk;
struct gpio_desc *data;
};
enum nsa320_inputs {
NSA320_TEMP = 0,
NSA320_FAN = 1,
};
static const char * const nsa320_input_names[] = {
[NSA320_TEMP] = "System Temperature",
[NSA320_FAN] = "Chassis Fan",
};
/*
* Although this protocol looks similar to SPI the long delay
* between the active (aka chip select) signal and the shorter
* delay between clock pulses are needed for reliable operation.
* The delays provided are taken from the manufacturer kernel,
* testing suggest they probably incorporate a reasonable safety
* margin. (The single device tested became unreliable if the
* delay was reduced to 1/10th of this value.)
*/
static s32 nsa320_hwmon_update(struct device *dev)
{
u32 mcu_data;
u32 mask;
struct nsa320_hwmon *hwmon = dev_get_drvdata(dev);
mutex_lock(&hwmon->update_lock);
mcu_data = hwmon->mcu_data;
if (time_after(jiffies, hwmon->last_updated + HZ) || mcu_data == 0) {
gpiod_set_value(hwmon->act, 1);
msleep(100);
mcu_data = 0;
for (mask = BIT(31); mask; mask >>= 1) {
gpiod_set_value(hwmon->clk, 0);
usleep_range(100, 200);
gpiod_set_value(hwmon->clk, 1);
usleep_range(100, 200);
if (gpiod_get_value(hwmon->data))
mcu_data |= mask;
}
gpiod_set_value(hwmon->act, 0);
dev_dbg(dev, "Read raw MCU data %08x\n", mcu_data);
if ((mcu_data >> 24) != MAGIC_NUMBER) {
dev_dbg(dev, "Read invalid MCU data %08x\n", mcu_data);
mcu_data = -EIO;
} else {
hwmon->mcu_data = mcu_data;
hwmon->last_updated = jiffies;
}
}
mutex_unlock(&hwmon->update_lock);
return mcu_data;
}
static ssize_t show_label(struct device *dev,
struct device_attribute *attr, char *buf)
{
int channel = to_sensor_dev_attr(attr)->index;
return sprintf(buf, "%s\n", nsa320_input_names[channel]);
}
static ssize_t show_temp(struct device *dev, struct device_attribute *attr,
char *buf)
{
s32 mcu_data = nsa320_hwmon_update(dev);
if (mcu_data < 0)
return mcu_data;
return sprintf(buf, "%d\n", (mcu_data & 0xffff) * 100);
}
static ssize_t show_fan(struct device *dev, struct device_attribute *attr,
char *buf)
{
s32 mcu_data = nsa320_hwmon_update(dev);
if (mcu_data < 0)
return mcu_data;
return sprintf(buf, "%d\n", ((mcu_data & 0xff0000) >> 16) * 100);
}
static SENSOR_DEVICE_ATTR(temp1_label, S_IRUGO, show_label, NULL, NSA320_TEMP);
static DEVICE_ATTR(temp1_input, S_IRUGO, show_temp, NULL);
static SENSOR_DEVICE_ATTR(fan1_label, S_IRUGO, show_label, NULL, NSA320_FAN);
static DEVICE_ATTR(fan1_input, S_IRUGO, show_fan, NULL);
static struct attribute *nsa320_attrs[] = {
&sensor_dev_attr_temp1_label.dev_attr.attr,
&dev_attr_temp1_input.attr,
&sensor_dev_attr_fan1_label.dev_attr.attr,
&dev_attr_fan1_input.attr,
NULL
};
ATTRIBUTE_GROUPS(nsa320);
static const struct of_device_id of_nsa320_hwmon_match[] = {
{ .compatible = "zyxel,nsa320-mcu", },
{ },
};
static int nsa320_hwmon_probe(struct platform_device *pdev)
{
struct nsa320_hwmon *hwmon;
struct device *classdev;
hwmon = devm_kzalloc(&pdev->dev, sizeof(*hwmon), GFP_KERNEL);
if (!hwmon)
return -ENOMEM;
/* Look up the GPIO pins to use */
hwmon->act = devm_gpiod_get(&pdev->dev, "act", GPIOD_OUT_LOW);
if (IS_ERR(hwmon->act))
return PTR_ERR(hwmon->act);
hwmon->clk = devm_gpiod_get(&pdev->dev, "clk", GPIOD_OUT_HIGH);
if (IS_ERR(hwmon->clk))
return PTR_ERR(hwmon->clk);
hwmon->data = devm_gpiod_get(&pdev->dev, "data", GPIOD_IN);
if (IS_ERR(hwmon->data))
return PTR_ERR(hwmon->data);
mutex_init(&hwmon->update_lock);
classdev = devm_hwmon_device_register_with_groups(&pdev->dev,
"nsa320", hwmon, nsa320_groups);
return PTR_ERR_OR_ZERO(classdev);
}
/* All allocations use devres so remove() is not needed. */
static struct platform_driver nsa320_hwmon_driver = {
.probe = nsa320_hwmon_probe,
.driver = {
.name = "nsa320-hwmon",
.of_match_table = of_match_ptr(of_nsa320_hwmon_match),
},
};
module_platform_driver(nsa320_hwmon_driver);
MODULE_DEVICE_TABLE(of, of_nsa320_hwmon_match);
MODULE_AUTHOR("Peter Schildmann <linux@schildmann.info>");
MODULE_AUTHOR("Adam Baker <linux@baker-net.org.uk>");
MODULE_DESCRIPTION("NSA320 Hardware Monitoring");
MODULE_LICENSE("GPL v2");
MODULE_ALIAS("platform:nsa320-hwmon");

View File

@ -54,6 +54,7 @@ static const struct platform_device_id ntc_thermistor_id[] = {
{ "ncp15wl333", TYPE_NCPXXWL333 },
{ "b57330v2103", TYPE_B57330V2103},
{ "ncp03wf104", TYPE_NCPXXWF104 },
{ "ncp15xh103", TYPE_NCPXXXH103 },
{ },
};
@ -173,6 +174,43 @@ static const struct ntc_compensation ncpXXwf104[] = {
{ .temp_c = 125, .ohm = 2522 },
};
static const struct ntc_compensation ncpXXxh103[] = {
{ .temp_c = -40, .ohm = 247565 },
{ .temp_c = -35, .ohm = 181742 },
{ .temp_c = -30, .ohm = 135128 },
{ .temp_c = -25, .ohm = 101678 },
{ .temp_c = -20, .ohm = 77373 },
{ .temp_c = -15, .ohm = 59504 },
{ .temp_c = -10, .ohm = 46222 },
{ .temp_c = -5, .ohm = 36244 },
{ .temp_c = 0, .ohm = 28674 },
{ .temp_c = 5, .ohm = 22878 },
{ .temp_c = 10, .ohm = 18399 },
{ .temp_c = 15, .ohm = 14910 },
{ .temp_c = 20, .ohm = 12169 },
{ .temp_c = 25, .ohm = 10000 },
{ .temp_c = 30, .ohm = 8271 },
{ .temp_c = 35, .ohm = 6883 },
{ .temp_c = 40, .ohm = 5762 },
{ .temp_c = 45, .ohm = 4851 },
{ .temp_c = 50, .ohm = 4105 },
{ .temp_c = 55, .ohm = 3492 },
{ .temp_c = 60, .ohm = 2985 },
{ .temp_c = 65, .ohm = 2563 },
{ .temp_c = 70, .ohm = 2211 },
{ .temp_c = 75, .ohm = 1915 },
{ .temp_c = 80, .ohm = 1666 },
{ .temp_c = 85, .ohm = 1454 },
{ .temp_c = 90, .ohm = 1275 },
{ .temp_c = 95, .ohm = 1121 },
{ .temp_c = 100, .ohm = 990 },
{ .temp_c = 105, .ohm = 876 },
{ .temp_c = 110, .ohm = 779 },
{ .temp_c = 115, .ohm = 694 },
{ .temp_c = 120, .ohm = 620 },
{ .temp_c = 125, .ohm = 556 },
};
/*
* The following compensation table is from the specification of EPCOS NTC
* Thermistors Datasheet
@ -260,6 +298,8 @@ static const struct of_device_id ntc_match[] = {
.data = &ntc_thermistor_id[5]},
{ .compatible = "murata,ncp03wf104",
.data = &ntc_thermistor_id[6] },
{ .compatible = "murata,ncp15xh103",
.data = &ntc_thermistor_id[7] },
/* Usage of vendor name "ntc" is deprecated */
{ .compatible = "ntc,ncp15wb473",
@ -609,6 +649,10 @@ static int ntc_thermistor_probe(struct platform_device *pdev)
data->comp = ncpXXwf104;
data->n_comp = ARRAY_SIZE(ncpXXwf104);
break;
case TYPE_NCPXXXH103:
data->comp = ncpXXxh103;
data->n_comp = ARRAY_SIZE(ncpXXxh103);
break;
default:
dev_err(&pdev->dev, "Unknown device type: %lu(%s)\n",
pdev_id->driver_data, pdev_id->name);

View File

@ -31,8 +31,8 @@ config SENSORS_ADM1275
default n
help
If you say yes here you get hardware monitoring support for Analog
Devices ADM1075, ADM1275, ADM1276, ADM1293, and ADM1294 Hot-Swap
Controller and Digital Power Monitors.
Devices ADM1075, ADM1275, ADM1276, ADM1278, ADM1293, and ADM1294
Hot-Swap Controller and Digital Power Monitors.
This driver can also be built as a module. If so, the module will
be called adm1275.

View File

@ -24,7 +24,7 @@
#include <linux/bitops.h>
#include "pmbus.h"
enum chips { adm1075, adm1275, adm1276, adm1293, adm1294 };
enum chips { adm1075, adm1275, adm1276, adm1278, adm1293, adm1294 };
#define ADM1275_MFR_STATUS_IOUT_WARN2 BIT(0)
#define ADM1293_MFR_STATUS_VAUX_UV_WARN BIT(5)
@ -41,6 +41,10 @@ enum chips { adm1075, adm1275, adm1276, adm1293, adm1294 };
#define ADM1075_IRANGE_25 BIT(3)
#define ADM1075_IRANGE_MASK (BIT(3) | BIT(4))
#define ADM1278_TEMP1_EN BIT(3)
#define ADM1278_VIN_EN BIT(2)
#define ADM1278_VOUT_EN BIT(1)
#define ADM1293_IRANGE_25 0
#define ADM1293_IRANGE_50 BIT(6)
#define ADM1293_IRANGE_100 BIT(7)
@ -54,6 +58,7 @@ enum chips { adm1075, adm1275, adm1276, adm1293, adm1294 };
#define ADM1293_VAUX_EN BIT(1)
#define ADM1278_PEAK_TEMP 0xd7
#define ADM1275_IOUT_WARN2_LIMIT 0xd7
#define ADM1275_DEVICE_CONFIG 0xd8
@ -80,6 +85,7 @@ struct adm1275_data {
bool have_iout_min;
bool have_pin_min;
bool have_pin_max;
bool have_temp_max;
struct pmbus_driver_info info;
};
@ -113,6 +119,13 @@ static const struct coefficients adm1276_coefficients[] = {
[4] = { 2115, 0, -1 }, /* power, vrange not set */
};
static const struct coefficients adm1278_coefficients[] = {
[0] = { 19599, 0, -2 }, /* voltage */
[1] = { 800, 20475, -1 }, /* current */
[2] = { 6123, 0, -2 }, /* power */
[3] = { 42, 31880, -1 }, /* temperature */
};
static const struct coefficients adm1293_coefficients[] = {
[0] = { 3333, -1, 0 }, /* voltage, vrange 1.2V */
[1] = { 5552, -5, -1 }, /* voltage, vrange 7.4V */
@ -196,6 +209,11 @@ static int adm1275_read_word_data(struct i2c_client *client, int page, int reg)
return -ENXIO;
ret = pmbus_read_word_data(client, 0, ADM1276_PEAK_PIN);
break;
case PMBUS_VIRT_READ_TEMP_MAX:
if (!data->have_temp_max)
return -ENXIO;
ret = pmbus_read_word_data(client, 0, ADM1278_PEAK_TEMP);
break;
case PMBUS_VIRT_RESET_IOUT_HISTORY:
case PMBUS_VIRT_RESET_VOUT_HISTORY:
case PMBUS_VIRT_RESET_VIN_HISTORY:
@ -204,6 +222,10 @@ static int adm1275_read_word_data(struct i2c_client *client, int page, int reg)
if (!data->have_pin_max)
return -ENXIO;
break;
case PMBUS_VIRT_RESET_TEMP_HISTORY:
if (!data->have_temp_max)
return -ENXIO;
break;
default:
ret = -ENODATA;
break;
@ -245,6 +267,9 @@ static int adm1275_write_word_data(struct i2c_client *client, int page, int reg,
ret = pmbus_write_word_data(client, 0,
ADM1293_PIN_MIN, 0);
break;
case PMBUS_VIRT_RESET_TEMP_HISTORY:
ret = pmbus_write_word_data(client, 0, ADM1278_PEAK_TEMP, 0);
break;
default:
ret = -ENODATA;
break;
@ -312,6 +337,7 @@ static const struct i2c_device_id adm1275_id[] = {
{ "adm1075", adm1075 },
{ "adm1275", adm1275 },
{ "adm1276", adm1276 },
{ "adm1278", adm1278 },
{ "adm1293", adm1293 },
{ "adm1294", adm1294 },
{ }
@ -329,6 +355,7 @@ static int adm1275_probe(struct i2c_client *client,
const struct i2c_device_id *mid;
const struct coefficients *coefficients;
int vindex = -1, voindex = -1, cindex = -1, pindex = -1;
int tindex = -1;
if (!i2c_check_functionality(client->adapter,
I2C_FUNC_SMBUS_READ_BYTE_DATA
@ -386,6 +413,7 @@ static int adm1275_probe(struct i2c_client *client,
info->format[PSC_VOLTAGE_OUT] = direct;
info->format[PSC_CURRENT_OUT] = direct;
info->format[PSC_POWER] = direct;
info->format[PSC_TEMPERATURE] = direct;
info->func[0] = PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT;
info->read_word_data = adm1275_read_word_data;
@ -460,6 +488,27 @@ static int adm1275_probe(struct i2c_client *client,
info->func[0] |=
PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT;
break;
case adm1278:
data->have_vout = true;
data->have_pin_max = true;
data->have_temp_max = true;
coefficients = adm1278_coefficients;
vindex = 0;
cindex = 1;
pindex = 2;
tindex = 3;
info->func[0] |= PMBUS_HAVE_PIN | PMBUS_HAVE_STATUS_INPUT;
if (config & ADM1278_TEMP1_EN)
info->func[0] |=
PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP;
if (config & ADM1278_VIN_EN)
info->func[0] |= PMBUS_HAVE_VIN;
if (config & ADM1278_VOUT_EN)
info->func[0] |=
PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT;
break;
case adm1293:
case adm1294:
data->have_iout_min = true;
@ -537,6 +586,11 @@ static int adm1275_probe(struct i2c_client *client,
info->b[PSC_POWER] = coefficients[pindex].b;
info->R[PSC_POWER] = coefficients[pindex].R;
}
if (tindex >= 0) {
info->m[PSC_TEMPERATURE] = coefficients[tindex].m;
info->b[PSC_TEMPERATURE] = coefficients[tindex].b;
info->R[PSC_TEMPERATURE] = coefficients[tindex].R;
}
return pmbus_do_probe(client, id, info);
}

View File

@ -28,6 +28,7 @@ enum ntc_thermistor_type {
TYPE_NCPXXWL333,
TYPE_B57330V2103,
TYPE_NCPXXWF104,
TYPE_NCPXXXH103,
};
struct ntc_thermistor_platform_data {