Commit 6729fb66 authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'hwmon-for-v5.4' of git://git.kernel.org/pub/scm/linux/kernel/git/groeck/linux-staging

Pull hwmon updates from Guenter Roeck:
 "New drivers:
   - Inspur Power System power supply driver
   - Synaptics AS370 PVT sensor driver

  Chip support:
   - support SHTC3 in shtc1 driver
   - support NCT6116 in nct6775 driver
   - support AMD family 17h, model 70h CPUs in k10temp driver
   - support PCT2075 in lm75 driver

  Removed drivers:
   - ads1015 driver (now supported in iio)

  Other changes:
   - Convert drivers to use devm_i2c_new_dummy_device
   - Substantial structural improvements in lm75 driver adding support
     for writing sample interval for supported chips
   - Add support for PSU version 2 to ibm-cffps driver
   - Add support for power attribute to iio_hwmon bridge
   - Add support for additional fan, voltage and temperature attributes
     to nct7904 driver
   - Convert adt7475 driver to use hwmon_device_register_with_groups()
   - Convert k8temp driver to use hwmon_device_register_with_info()
   - Various other improvements and minor fixes"

* tag 'hwmon-for-v5.4' of git://git.kernel.org/pub/scm/linux/kernel/git/groeck/linux-staging: (48 commits)
  hwmon: submitting-patches: Add note on comment style
  hwmon: submitting-patches: Point to with_info API
  hwmon: (nct7904) Fix incorrect SMI status register setting of LTD temperature and fan.
  hwmon: (shtc1) add support for the SHTC3 sensor
  hwmon: (shtc1) fix shtc1 and shtw1 id mask
  hwmon: (lm75) Aproximate sample times to data-sheet values
  hwmon: (w83793d) convert to use devm_i2c_new_dummy_device
  hwmon: (w83792d) convert to use devm_i2c_new_dummy_device
  hwmon: (w83791d) convert to use devm_i2c_new_dummy_device
  hwmon: (as370-hwmon) fix devm_platform_ioremap_resource.cocci warnings
  hwmon: (lm75) Add support for writing sampling period on PCT2075
  hwmon: (lm75) Add support for writing conversion time for TMP112
  hwmon: (lm75) Move updating the sample interval to its own function
  hwmon: (lm75) Support configuring the sample time for various chips
  hwmon: (nct7904) Fix incorrect temperature limitation register setting of LTD.
  hwmon: (as370-hwmon) Add DT bindings for Synaptics AS370 PVT
  hwmon: Add Synaptics AS370 PVT sensor driver
  pmbus: (ibm-cffps) Add support for version 2 of the PSU
  dt-bindings: hwmon: Document ibm,cffps2 compatible string
  hwmon: (iio_hwmon) Enable power exporting from IIO
  ...
parents 8e97be2a 4e19e72f
Bindings for Synaptics AS370 PVT sensors
Required properties:
- compatible : "syna,as370-hwmon"
- reg : address and length of the register set.
Example:
hwmon@ea0810 {
compatible = "syna,as370-hwmon";
reg = <0xea0810 0xc>;
};
Device-tree bindings for IBM Common Form Factor Power Supply Version 1
----------------------------------------------------------------------
Device-tree bindings for IBM Common Form Factor Power Supply Versions 1 and 2
-----------------------------------------------------------------------------
Required properties:
- compatible = "ibm,cffps1";
- compatible : Must be one of the following:
"ibm,cffps1"
"ibm,cffps2"
- reg = < I2C bus address >; : Address of the power supply on the
I2C bus.
......
......@@ -15,6 +15,7 @@ Required properties:
"maxim,max31725",
"maxim,max31726",
"maxim,mcp980x",
"nxp,pct2075",
"st,stds75",
"st,stlm75",
"microchip,tcn75",
......
......@@ -104,6 +104,8 @@ properties:
- infineon,slb9645tt
# Infineon TLV493D-A1B6 I2C 3D Magnetic Sensor
- infineon,tlv493d-a1b6
# Inspur Power System power supply unit version 1
- inspur,ipsps1
# Intersil ISL29028 Ambient Light and Proximity Sensor
- isil,isl29028
# Intersil ISL29030 Ambient Light and Proximity Sensor
......
Kernel driver ads1015
=====================
Supported chips:
* Texas Instruments ADS1015
Prefix: 'ads1015'
Datasheet: Publicly available at the Texas Instruments website:
http://focus.ti.com/lit/ds/symlink/ads1015.pdf
* Texas Instruments ADS1115
Prefix: 'ads1115'
Datasheet: Publicly available at the Texas Instruments website:
http://focus.ti.com/lit/ds/symlink/ads1115.pdf
Authors:
Dirk Eibach, Guntermann & Drunck GmbH <eibach@gdsys.de>
Description
-----------
This driver implements support for the Texas Instruments ADS1015/ADS1115.
This device is a 12/16-bit A-D converter with 4 inputs.
The inputs can be used single ended or in certain differential combinations.
The inputs can be made available by 8 sysfs input files in0_input - in7_input:
- in0: Voltage over AIN0 and AIN1.
- in1: Voltage over AIN0 and AIN3.
- in2: Voltage over AIN1 and AIN3.
- in3: Voltage over AIN2 and AIN3.
- in4: Voltage over AIN0 and GND.
- in5: Voltage over AIN1 and GND.
- in6: Voltage over AIN2 and GND.
- in7: Voltage over AIN3 and GND.
Which inputs are available can be configured using platform data or devicetree.
By default all inputs are exported.
Platform Data
-------------
In linux/platform_data/ads1015.h platform data is defined, channel_data contains
configuration data for the used input combinations:
- pga is the programmable gain amplifier (values are full scale)
- 0: +/- 6.144 V
- 1: +/- 4.096 V
- 2: +/- 2.048 V
- 3: +/- 1.024 V
- 4: +/- 0.512 V
- 5: +/- 0.256 V
- data_rate in samples per second
- 0: 128
- 1: 250
- 2: 490
- 3: 920
- 4: 1600
- 5: 2400
- 6: 3300
Example::
struct ads1015_platform_data data = {
.channel_data = {
[2] = { .enabled = true, .pga = 1, .data_rate = 0 },
[4] = { .enabled = true, .pga = 4, .data_rate = 5 },
}
};
In this case only in2_input (FS +/- 4.096 V, 128 SPS) and in4_input
(FS +/- 0.512 V, 2400 SPS) would be created.
Devicetree
----------
Configuration is also possible via devicetree:
Documentation/devicetree/bindings/hwmon/ads1015.txt
......@@ -30,7 +30,6 @@ Hardware Monitoring Kernel Drivers
adm1031
adm1275
adm9240
ads1015
ads7828
adt7410
adt7411
......@@ -130,6 +129,7 @@ Hardware Monitoring Kernel Drivers
pcf8591
pmbus
powr1220
pxe1610
pwm-fan
raspberrypi-hwmon
sch5627
......
Kernel driver inspur-ipsps1
=======================
Supported chips:
* Inspur Power System power supply unit
Author: John Wang <wangzqbj@inspur.com>
Description
-----------
This driver supports Inspur Power System power supplies. This driver
is a client to the core PMBus driver.
Usage Notes
-----------
This driver does not auto-detect devices. You will have to instantiate the
devices explicitly. Please see Documentation/i2c/instantiating-devices for
details.
Sysfs entries
-------------
The following attributes are supported:
======================= ======================================================
curr1_input Measured input current
curr1_label "iin"
curr1_max Maximum current
curr1_max_alarm Current high alarm
curr2_input Measured output current in mA.
curr2_label "iout1"
curr2_crit Critical maximum current
curr2_crit_alarm Current critical high alarm
curr2_max Maximum current
curr2_max_alarm Current high alarm
fan1_alarm Fan 1 warning.
fan1_fault Fan 1 fault.
fan1_input Fan 1 speed in RPM.
in1_alarm Input voltage under-voltage alarm.
in1_input Measured input voltage in mV.
in1_label "vin"
in2_input Measured output voltage in mV.
in2_label "vout1"
in2_lcrit Critical minimum output voltage
in2_lcrit_alarm Output voltage critical low alarm
in2_max Maximum output voltage
in2_max_alarm Output voltage high alarm
in2_min Minimum output voltage
in2_min_alarm Output voltage low alarm
power1_alarm Input fault or alarm.
power1_input Measured input power in uW.
power1_label "pin"
power1_max Input power limit
power2_max_alarm Output power high alarm
power2_max Output power limit
power2_input Measured output power in uW.
power2_label "pout"
temp[1-3]_input Measured temperature
temp[1-2]_max Maximum temperature
temp[1-3]_max_alarm Temperature high alarm
vendor Manufacturer name
model Product model
part_number Product part number
serial_number Product serial number
fw_version Firmware version
hw_version Hardware version
mode Work mode. Can be set to active or
standby, when set to standby, PSU will
automatically switch between standby
and redundancy mode.
======================= ======================================================
......@@ -119,9 +119,9 @@ Supported chips:
http://www.ti.com/product/tmp275
* NXP LM75B
* NXP LM75B, PCT2075
Prefix: 'lm75b'
Prefix: 'lm75b', 'pct2075'
Addresses scanned: none
......@@ -129,6 +129,8 @@ Supported chips:
http://www.nxp.com/documents/data_sheet/LM75B.pdf
http://www.nxp.com/docs/en/data-sheet/PCT2075.pdf
Author: Frodo Looijaard <frodol@dds.nl>
Description
......
......@@ -2,19 +2,29 @@ Kernel driver pxe1610
=====================
Supported chips:
* Infineon PXE1610
Prefix: 'pxe1610'
Addresses scanned: -
Datasheet: Datasheet is not publicly available.
* Infineon PXE1110
Prefix: 'pxe1110'
Addresses scanned: -
Datasheet: Datasheet is not publicly available.
* Infineon PXM1310
Prefix: 'pxm1310'
Addresses scanned: -
Datasheet: Datasheet is not publicly available.
Author: Vijay Khemka <vijaykhemka@fb.com>
......@@ -25,14 +35,19 @@ Description
PXE1610/PXE1110 are Multi-rail/Multiphase Digital Controllers
and compliant to
-- Intel VR13 DC-DC converter specifications.
-- Intel SVID protocol.
- Intel VR13 DC-DC converter specifications.
- Intel SVID protocol.
Used for Vcore power regulation for Intel VR13 based microprocessors
-- Servers, Workstations, and High-end desktops
- Servers, Workstations, and High-end desktops
PXM1310 is a Multi-rail Controller and it is compliant to
-- Intel VR13 DC-DC converter specifications.
-- Intel SVID protocol.
- Intel VR13 DC-DC converter specifications.
- Intel SVID protocol.
Used for DDR3/DDR4 Memory power regulation for Intel VR13 and
IMVP8 based systems
......@@ -44,10 +59,10 @@ This driver does not probe for PMBus devices. You will have
to instantiate devices explicitly.
Example: the following commands will load the driver for an PXE1610
at address 0x70 on I2C bus #4:
at address 0x70 on I2C bus #4::
# modprobe pxe1610
# echo pxe1610 0x70 > /sys/bus/i2c/devices/i2c-4/new_device
# modprobe pxe1610
# echo pxe1610 0x70 > /sys/bus/i2c/devices/i2c-4/new_device
It can also be instantiated by declaring in device tree
......@@ -55,6 +70,7 @@ It can also be instantiated by declaring in device tree
Sysfs attributes
----------------
====================== ====================================
curr1_label "iin"
curr1_input Measured input current
curr1_alarm Current high alarm
......@@ -88,3 +104,4 @@ temp[1-3]_crit Critical high temperature
temp[1-3]_crit_alarm Chip temperature critical high alarm
temp[1-3]_max Maximum temperature
temp[1-3]_max_alarm Chip temperature high alarm
====================== ====================================
......@@ -19,7 +19,17 @@ Supported chips:
Addresses scanned: none
Datasheet: Not publicly available
Datasheet: http://www.sensirion.com/file/datasheet_shtw1
* Sensirion SHTC3
Prefix: 'shtc3'
Addresses scanned: none
Datasheet: http://www.sensirion.com/file/datasheet_shtc3
......@@ -30,10 +40,9 @@ Author:
Description
-----------
This driver implements support for the Sensirion SHTC1 chip, a humidity and
temperature sensor. Temperature is measured in degrees celsius, relative
humidity is expressed as a percentage. Driver can be used as well for SHTW1
chip, which has the same electrical interface.
This driver implements support for the Sensirion SHTC1, SHTW1, and SHTC3
chips, a humidity and temperature sensor. Temperature is measured in degrees
celsius, relative humidity is expressed as a percentage.
The device communicates with the I2C protocol. All sensors are set to I2C
address 0x70. See Documentation/i2c/instantiating-devices for methods to
......
......@@ -20,6 +20,10 @@ increase the chances of your change being accepted.
errors, no warnings, and few if any check messages. If there are any
messages, please be prepared to explain.
* Please use the standard multi-line comment style. Do not mix C and C++
style comments in a single driver (with the exception of the SPDX license
identifier).
* 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,
......@@ -120,8 +124,8 @@ increase the chances of your change being accepted.
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
* Use devm_hwmon_device_register_with_info() or, if your driver needs a remove
function, hwmon_device_register_with_info() 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().
......
......@@ -517,14 +517,6 @@ W: http://ez.analog.com/community/linux-device-drivers
S: Supported
F: drivers/video/backlight/adp8860_bl.c
ADS1015 HARDWARE MONITOR DRIVER
M: Dirk Eibach <eibach@gdsys.de>
L: linux-hwmon@vger.kernel.org
S: Maintained
F: Documentation/hwmon/ads1015.rst
F: drivers/hwmon/ads1015.c
F: include/linux/platform_data/ads1015.h
ADT746X FAN DRIVER
M: Colin Leroy <colin@colino.net>
S: Maintained
......
......@@ -21,6 +21,7 @@
#define PCI_DEVICE_ID_AMD_17H_DF_F4 0x1464
#define PCI_DEVICE_ID_AMD_17H_M10H_DF_F4 0x15ec
#define PCI_DEVICE_ID_AMD_17H_M30H_DF_F4 0x1494
#define PCI_DEVICE_ID_AMD_17H_M70H_DF_F4 0x1444
/* Protect the PCI config register pairs used for SMN and DF indirect access. */
static DEFINE_MUTEX(smn_mutex);
......@@ -50,6 +51,7 @@ const struct pci_device_id amd_nb_misc_ids[] = {
{ PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_17H_M10H_DF_F3) },
{ PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_17H_M30H_DF_F3) },
{ PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_CNB17H_F3) },
{ PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_17H_M70H_DF_F3) },
{}
};
EXPORT_SYMBOL_GPL(amd_nb_misc_ids);
......@@ -63,6 +65,7 @@ static const struct pci_device_id amd_nb_link_ids[] = {
{ PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_17H_DF_F4) },
{ PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_17H_M10H_DF_F4) },
{ PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_17H_M30H_DF_F4) },
{ PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_17H_M70H_DF_F4) },
{ PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_CNB17H_F4) },
{}
};
......
......@@ -246,6 +246,16 @@ config SENSORS_ADT7475
This driver can also be built as a module. If so, the module
will be called adt7475.
config SENSORS_AS370
tristate "Synaptics AS370 SoC hardware monitoring driver"
help
If you say yes here you get support for the PVT sensors of
the Synaptics AS370 SoC
This driver can also be built as a module. If so, the module
will be called as370-hwmon.
config SENSORS_ASC7621
tristate "Andigilog aSC7621"
depends on I2C
......@@ -1382,8 +1392,8 @@ config SENSORS_SHTC1
tristate "Sensiron humidity and temperature sensors. SHTC1 and compat."
depends on I2C
help
If you say yes here you get support for the Sensiron SHTC1 and SHTW1
humidity and temperature sensors.
If you say yes here you get support for the Sensiron SHTC1, SHTW1,
and SHTC3 humidity and temperature sensors.
This driver can also be built as a module. If so, the module
will be called shtc1.
......@@ -1570,16 +1580,6 @@ config SENSORS_ADC128D818
This driver can also be built as a module. If so, the module
will be called adc128d818.
config SENSORS_ADS1015
tristate "Texas Instruments ADS1015"
depends on I2C
help
If you say yes here you get support for Texas Instruments
ADS1015/ADS1115 12/16-bit 4-input ADC device.
This driver can also be built as a module. If so, the module
will be called ads1015.
config SENSORS_ADS7828
tristate "Texas Instruments ADS7828 and compatibles"
depends on I2C
......@@ -1834,17 +1834,12 @@ config SENSORS_W83795
will be called w83795.
config SENSORS_W83795_FANCTRL
bool "Include automatic fan control support (DANGEROUS)"
bool "Include automatic fan control support"
depends on SENSORS_W83795
help
If you say yes here, support for automatic fan speed control
will be included in the driver.
This part of the code wasn't carefully reviewed and tested yet,
so enabling this option is strongly discouraged on production
servers. Only developers and testers should enable it for the
time being.
Please also note that this option will create sysfs attribute
files which may change in the future, so you shouldn't rely
on them being stable.
......
......@@ -35,7 +35,6 @@ obj-$(CONFIG_SENSORS_ADM1026) += adm1026.o
obj-$(CONFIG_SENSORS_ADM1029) += adm1029.o
obj-$(CONFIG_SENSORS_ADM1031) += adm1031.o
obj-$(CONFIG_SENSORS_ADM9240) += adm9240.o
obj-$(CONFIG_SENSORS_ADS1015) += ads1015.o
obj-$(CONFIG_SENSORS_ADS7828) += ads7828.o
obj-$(CONFIG_SENSORS_ADS7871) += ads7871.o
obj-$(CONFIG_SENSORS_ADT7X10) += adt7x10.o
......@@ -48,6 +47,7 @@ obj-$(CONFIG_SENSORS_ADT7475) += adt7475.o
obj-$(CONFIG_SENSORS_APPLESMC) += applesmc.o
obj-$(CONFIG_SENSORS_ARM_SCMI) += scmi-hwmon.o
obj-$(CONFIG_SENSORS_ARM_SCPI) += scpi-hwmon.o
obj-$(CONFIG_SENSORS_AS370) += as370-hwmon.o
obj-$(CONFIG_SENSORS_ASC7621) += asc7621.o
obj-$(CONFIG_SENSORS_ASPEED) += aspeed-pwm-tacho.o
obj-$(CONFIG_SENSORS_ATXP1) += atxp1.o
......
......@@ -681,8 +681,8 @@ static int setup_attrs(struct acpi_power_meter_resource *resource)
if (resource->caps.flags & POWER_METER_CAN_CAP) {
if (!can_cap_in_hardware()) {
dev_err(&resource->acpi_dev->dev,
"Ignoring unsafe software power cap!\n");
dev_warn(&resource->acpi_dev->dev,
"Ignoring unsafe software power cap!\n");
goto skip_unsafe_cap;
}
......
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* ads1015.c - lm_sensors driver for ads1015 12-bit 4-input ADC
* (C) Copyright 2010
* Dirk Eibach, Guntermann & Drunck GmbH <eibach@gdsys.de>
*
* Based on the ads7828 driver by Steve Hardy.
*
* Datasheet available at: http://focus.ti.com/lit/ds/symlink/ads1015.pdf
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/delay.h>
#include <linux/i2c.h>
#include <linux/hwmon.h>
#include <linux/hwmon-sysfs.h>
#include <linux/err.h>
#include <linux/mutex.h>
#include <linux/of_device.h>
#include <linux/of.h>
#include <linux/platform_data/ads1015.h>
/* ADS1015 registers */
enum {
ADS1015_CONVERSION = 0,
ADS1015_CONFIG = 1,
};
/* PGA fullscale voltages in mV */
static const unsigned int fullscale_table[8] = {
6144, 4096, 2048, 1024, 512, 256, 256, 256 };
/* Data rates in samples per second */
static const unsigned int data_rate_table_1015[8] = {
128, 250, 490, 920, 1600, 2400, 3300, 3300
};
static const unsigned int data_rate_table_1115[8] = {
8, 16, 32, 64, 128, 250, 475, 860
};
#define ADS1015_DEFAULT_CHANNELS 0xff
#define ADS1015_DEFAULT_PGA 2
#define ADS1015_DEFAULT_DATA_RATE 4
enum ads1015_chips {
ads1015,
ads1115,
};
struct ads1015_data {
struct device *hwmon_dev;
struct mutex update_lock; /* mutex protect updates */
struct ads1015_channel_data channel_data[ADS1015_CHANNELS];
enum ads1015_chips id;
};
static int ads1015_read_adc(struct i2c_client *client, unsigned int channel)
{
u16 config;
struct ads1015_data *data = i2c_get_clientdata(client);
unsigned int pga = data->channel_data[channel].pga;
unsigned int data_rate = data->channel_data[channel].data_rate;
unsigned int conversion_time_ms;
const unsigned int * const rate_table = data->id == ads1115 ?
data_rate_table_1115 : data_rate_table_1015;
int res;
mutex_lock(&data->update_lock);
/* get channel parameters */
res = i2c_smbus_read_word_swapped(client, ADS1015_CONFIG);
if (res < 0)
goto err_unlock;
config = res;
conversion_time_ms = DIV_ROUND_UP(1000, rate_table[data_rate]);
/* setup and start single conversion */
config &= 0x001f;
config |= (1 << 15) | (1 << 8);
config |= (channel & 0x0007) << 12;
config |= (pga & 0x0007) << 9;
config |= (data_rate & 0x0007) << 5;
res = i2c_smbus_write_word_swapped(client, ADS1015_CONFIG, config);
if (res < 0)
goto err_unlock;
/* wait until conversion finished */
msleep(conversion_time_ms);
res = i2c_smbus_read_word_swapped(client, ADS1015_CONFIG);
if (res < 0)
goto err_unlock;
config = res;
if (!(config & (1 << 15))) {
/* conversion not finished in time */
res = -EIO;
goto err_unlock;
}
res = i2c_smbus_read_word_swapped(client, ADS1015_CONVERSION);
err_unlock:
mutex_unlock(&data->update_lock);
return res;
}
static int ads1015_reg_to_mv(struct i2c_client *client, unsigned int channel,
s16 reg)
{
struct ads1015_data *data = i2c_get_clientdata(client);
unsigned int pga = data->channel_data[channel].pga;
int fullscale = fullscale_table[pga];
const int mask = data->id == ads1115 ? 0x7fff : 0x7ff0;
return DIV_ROUND_CLOSEST(reg * fullscale, mask);
}
/* sysfs callback function */
static ssize_t in_show(struct device *dev, struct device_attribute *da,
char *buf)
{
struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
struct i2c_client *client = to_i2c_client(dev);
int res;
int index = attr->index;
res = ads1015_read_adc(client, index);
if (res < 0)
return res;
return sprintf(buf, "%d\n", ads1015_reg_to_mv(client, index, res));
}
static const struct sensor_device_attribute ads1015_in[] = {
SENSOR_ATTR_RO(in0_input, in, 0),
SENSOR_ATTR_RO(in1_input, in, 1),
SENSOR_ATTR_RO(in2_input, in, 2),
SENSOR_ATTR_RO(in3_input, in, 3),
SENSOR_ATTR_RO(in4_input, in, 4),
SENSOR_ATTR_RO(in5_input, in, 5),
SENSOR_ATTR_RO(in6_input, in, 6),
SENSOR_ATTR_RO(in7_input, in, 7),
};
/*
* Driver interface
*/
static int ads1015_remove(struct i2c_client *client)
{
struct ads1015_data *data = i2c_get_clientdata(client);
int k;
hwmon_device_unregister(data->hwmon_dev);
for (k = 0; k < ADS1015_CHANNELS; ++k)
device_remove_file(&client->dev, &ads1015_in[k].dev_attr);
return 0;
}
#ifdef CONFIG_OF
static int ads1015_get_channels_config_of(struct i2c_client *client)
{
struct ads1015_data *data = i2c_get_clientdata(client);
struct device_node *node;
if (!client->dev.of_node
|| !of_get_next_child(client->dev.of_node, NULL))
return -EINVAL;
for_each_child_of_node(client->dev.of_node, node) {
u32 pval;
unsigned int channel;
unsigned int pga = ADS1015_DEFAULT_PGA;
unsigned int data_rate = ADS1015_DEFAULT_DATA_RATE;
if (of_property_read_u32(node, "reg", &pval)) {
dev_err(&client->dev, "invalid reg on %pOF\n", node);
continue;
}
channel = pval;
if (channel >= ADS1015_CHANNELS) {
dev_err(&client->dev,
"invalid channel index %d on %pOF\n",
channel, node);
continue;
}
if (!of_property_read_u32(node, "ti,gain", &pval)) {
pga = pval;
if (pga > 6) {
dev_err(&client->dev, "invalid gain on %pOF\n",
node);
return -EINVAL;
}
}
if (!of_property_read_u32(node, "ti,datarate", &pval)) {
data_rate = pval;
if (data_rate > 7) {
dev_err(&client->dev,
"invalid data_rate on %pOF\n", node);
return -EINVAL;
}
}
data->channel_data[channel].enabled = true;
data->channel_data[channel].pga = pga;
data->channel_data[channel].data_rate = data_rate;
}
return 0;
}
#endif
static void ads1015_get_channels_config(struct i2c_client *client)
{
unsigned int k;
struct ads1015_data *data = i2c_get_clientdata(client);
struct ads1015_platform_data *pdata = dev_get_platdata(&client->dev);
/* prefer platform data */
if (pdata) {
memcpy(data->channel_data, pdata->channel_data,
sizeof(data->channel_data));
return;
}
#ifdef CONFIG_OF
if (!ads1015_get_channels_config_of(client))
return;
#endif
/* fallback on default configuration */
for (k = 0; k < ADS1015_CHANNELS; ++k) {
data->channel_data[k].enabled = true;
data->channel_data[k].pga = ADS1015_DEFAULT_PGA;
data->channel_data[k].data_rate = ADS1015_DEFAULT_DATA_RATE;
}
}
static int ads1015_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct ads1015_data *data;
int err;
unsigned int k;
data = devm_kzalloc(&client->dev, sizeof(struct ads1015_data),
GFP_KERNEL);
if (!data)
return -ENOMEM;
if (client->dev.of_node)
data->id = (enum ads1015_chips)
of_device_get_match_data(&client->dev);
else
data->id = id->driver_data;
i2c_set_clientdata(client, data);
mutex_init(&data->update_lock);
/* build sysfs attribute group */
ads1015_get_channels_config(client);
for (k = 0; k < ADS1015_CHANNELS; ++k) {
if (!data->channel_data[k].enabled)
continue;
err = device_create_file(&client->dev, &ads1015_in[k].dev_attr);
if (err)
goto exit_remove;
}
data->hwmon_dev = hwmon_device_register(&client->dev);
if (IS_ERR(data->hwmon_dev)) {
err = PTR_ERR(data->hwmon_dev);
goto exit_remove;
}
return 0;
exit_remove:
for (k = 0; k < ADS1015_CHANNELS; ++k)
device_remove_file(&client->dev, &ads1015_in[k].dev_attr);
return err;
}
static const struct i2c_device_id ads1015_id[] = {
{ "ads1015", ads1015},
{ "ads1115", ads1115},
{ }
};
MODULE_DEVICE_TABLE(i2c, ads1015_id);
static const struct of_device_id __maybe_unused ads1015_of_match[] = {
{
.compatible = "ti,ads1015",
.data = (void *)ads1015
},
{
.compatible = "ti,ads1115",
.data = (void *)ads1115
},
{ },
};
MODULE_DEVICE_TABLE(of, ads1015_of_match);
static struct i2c_driver ads1015_driver = {
.driver = {
.name = "ads1015",
.of_match_table = of_match_ptr(ads1015_of_match),
},
.probe = ads1015_probe,
.remove = ads1015_remove,
.id_table = ads1015_id,
};
module_i2c_driver(ads1015_driver);
MODULE_AUTHOR("Dirk Eibach <eibach@gdsys.de>");
MODULE_DESCRIPTION("ADS1015 driver");
MODULE_LICENSE("GPL");
......@@ -187,7 +187,7 @@ static const struct of_device_id __maybe_unused adt7475_of_match[] = {
MODULE_DEVICE_TABLE(of, adt7475_of_match);
struct adt7475_data {
struct device *hwmon_dev;
struct i2c_client *client;
struct mutex lock;
unsigned long measure_updated;
......@@ -212,6 +212,7 @@ struct adt7475_data {
u8 vid;
u8 vrm;
const struct attribute_group *groups[9];
};
static struct i2c_driver adt7475_driver;
......@@ -346,8 +347,8 @@ static ssize_t voltage_store(struct device *dev,
{
struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr);
struct i2c_client *client = to_i2c_client(dev);
struct adt7475_data *data = i2c_get_clientdata(client);
struct adt7475_data *data = dev_get_drvdata(dev);
struct i2c_client *client = data->client;
unsigned char reg;
long val;
......@@ -440,8 +441,8 @@ static ssize_t temp_store(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr);
struct i2c_client *client = to_i2c_client(dev);
struct adt7475_data *data = i2c_get_clientdata(client);
struct adt7475_data *data = dev_get_drvdata(dev);
struct i2c_client *client = data->client;
unsigned char reg = 0;
u8 out;
int temp;
......@@ -542,8 +543,7 @@ static ssize_t temp_st_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr);
struct i2c_client *client = to_i2c_client(dev);
struct adt7475_data *data = i2c_get_clientdata(client);
struct adt7475_data *data = dev_get_drvdata(dev);
long val;
switch (sattr->index) {
......@@ -570,8 +570,8 @@ static ssize_t temp_st_store(struct device *dev,
size_t count)
{
struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr);
struct i2c_client *client = to_i2c_client(dev);
struct adt7475_data *data = i2c_get_clientdata(client);
struct adt7475_data *data = dev_get_drvdata(dev);
struct i2c_client *client = data->client;
unsigned char reg;
int shift, idx;
ulong val;
......@@ -647,8 +647,8 @@ static ssize_t point2_show(struct device *dev, struct device_attribute *attr,
static ssize_t point2_store(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
struct i2c_client *client = to_i2c_client(dev);
struct adt7475_data *data = i2c_get_clientdata(client);
struct adt7475_data *data = dev_get_drvdata(dev);
struct i2c_client *client = data->client;
struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr);
int temp;
long val;
......@@ -710,8 +710,8 @@ static ssize_t tach_store(struct device *dev, struct device_attribute *attr,
{
struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr);
struct i2c_client *client = to_i2c_client(dev);
struct adt7475_data *data = i2c_get_clientdata(client);
struct adt7475_data *data = dev_get_drvdata(dev);
struct i2c_client *client = data->client;
unsigned long val;
if (kstrtoul(buf, 10, &val))
......@@ -769,8 +769,8 @@ static ssize_t pwm_store(struct device *dev, struct device_attribute *attr,
{
struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr);
struct i2c_client *client = to_i2c_client(dev);
struct adt7475_data *data = i2c_get_clientdata(client);
struct adt7475_data *data = dev_get_drvdata(dev);
struct i2c_client *client = data->client;
unsigned char reg = 0;
long val;
......@@ -818,8 +818,8 @@ static ssize_t stall_disable_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr);
struct i2c_client *client = to_i2c_client(dev);
struct adt7475_data *data = i2c_get_clientdata(client);
struct adt7475_data *data = dev_get_drvdata(dev);
u8 mask = BIT(5 + sattr->index);
return sprintf(buf, "%d\n", !!(data->enh_acoustics[0] & mask));
......@@ -830,8 +830,8 @@ static ssize_t stall_disable_store(struct device *dev,
const char *buf, size_t count)
{
struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr);
struct i2c_client *client = to_i2c_client(dev);
struct adt7475_data *data = i2c_get_clientdata(client);
struct adt7475_data *data = dev_get_drvdata(dev);
struct i2c_client *client = data->client;
long val;
u8 mask = BIT(5 + sattr->index);
......@@ -914,8 +914,8 @@ static ssize_t pwmchan_store(struct device *dev,
size_t count)
{
struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr);
struct i2c_client *client = to_i2c_client(dev);
struct adt7475_data *data = i2c_get_clientdata(client);
struct adt7475_data *data = dev_get_drvdata(dev);
struct i2c_client *client = data->client;
int r;
long val;
......@@ -938,8 +938,8 @@ static ssize_t pwmctrl_store(struct device *dev,
size_t count)
{
struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr);
struct i2c_client *client = to_i2c_client(dev);
struct adt7475_data *data = i2c_get_clientdata(client);
struct adt7475_data *data = dev_get_drvdata(dev);
struct i2c_client *client = data->client;
int r;
long val;
......@@ -982,8 +982,8 @@ static ssize_t pwmfreq_store(struct device *dev,
size_t count)
{
struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr);
struct i2c_client *client = to_i2c_client(dev);
struct adt7475_data *data = i2c_get_clientdata(client);
struct adt7475_data *data = dev_get_drvdata(dev);
struct i2c_client *client = data->client;
int out;
long val;
......@@ -1022,8 +1022,8 @@ static ssize_t pwm_use_point2_pwm_at_crit_store(struct device *dev,
struct device_attribute *devattr,
const char *buf, size_t count)
{
struct i2c_client *client = to_i2c_client(dev);
struct adt7475_data *data = i2c_get_clientdata(client);
struct adt7475_data *data = dev_get_drvdata(dev);
struct i2c_client *client = data->client;
long val;
if (kstrtol(buf, 10, &val))
......@@ -1342,26 +1342,6 @@ static int adt7475_detect(struct i2c_client *client,
return 0;
}
static void adt7475_remove_files(struct i2c_client *client,
struct adt7475_data *data)
{
sysfs_remove_group(&client->dev.kobj, &adt7475_attr_group);
if (data->has_fan4)
sysfs_remove_group(&client->dev.kobj, &fan4_attr_group);
if (data->has_pwm2)
sysfs_remove_group(&client->dev.kobj, &pwm2_attr_group);
if (data->has_voltage & (1 << 0))
sysfs_remove_group(&client->dev.kobj, &in0_attr_group);
if (data->has_voltage & (1 << 3))
sysfs_remove_group(&client->dev.kobj, &in3_attr_group);
if (data->has_voltage & (1 << 4))
sysfs_remove_group(&client->dev.kobj, &in4_attr_group);
if (data->has_voltage & (1 << 5))
sysfs_remove_group(&client->dev.kobj, &in5_attr_group);
if (data->has_vid)
sysfs_remove_group(&client->dev.kobj, &vid_attr_group);
}
static int adt7475_update_limits(struct i2c_client *client)
{
struct adt7475_data *data = i2c_get_clientdata(client);
......@@ -1489,7 +1469,8 @@ static int adt7475_probe(struct i2c_client *client,
};
struct adt7475_data *data;
int i, ret = 0, revision;
struct device *hwmon_dev;
int i, ret = 0, revision, group_num = 0;
u8 config2, config3;
data = devm_kzalloc(&client->dev, sizeof(*data), GFP_KERNEL);
......@@ -1497,6 +1478,7 @@ static int adt7475_probe(struct i2c_client *client,
return -ENOMEM;
mutex_init(&data->lock);
data->client = client;
i2c_set_clientdata(client, data);
if (client->dev.of_node)
......@@ -1590,52 +1572,40 @@ static int adt7475_probe(struct i2c_client *client,
break;
}
ret = sysfs_create_group(&client->dev.kobj, &adt7475_attr_group);
if (ret)
return ret;
data->groups[group_num++] = &adt7475_attr_group;
/* Features that can be disabled individually */
if (data->has_fan4) {
ret = sysfs_create_group(&client->dev.kobj, &fan4_attr_group);
if (ret)
goto eremove;
data->groups[group_num++] = &fan4_attr_group;
}
if (data->has_pwm2) {
ret = sysfs_create_group(&client->dev.kobj, &pwm2_attr_group);
if (ret)
goto eremove;
data->groups[group_num++] = &pwm2_attr_group;
}
if (data->has_voltage & (1 << 0)) {
ret = sysfs_create_group(&client->dev.kobj, &in0_attr_group);
if (ret)
goto eremove;
data->groups[group_num++] = &in0_attr_group;
}
if (data->has_voltage & (1 << 3)) {
ret = sysfs_create_group(&client->dev.kobj, &in3_attr_group);
if (ret)
goto eremove;
data->groups[group_num++] = &in3_attr_group;
}
if (data->has_voltage & (1 << 4)) {
ret = sysfs_create_group(&client->dev.kobj, &in4_attr_group);
if (ret)
goto eremove;
data->groups[group_num++] = &in4_attr_group;
}
if (data->has_voltage & (1 << 5)) {
ret = sysfs_create_group(&client->dev.kobj, &in5_attr_group);
if (ret)
goto eremove;
data->groups[group_num++] = &in5_attr_group;
}
if (data->has_vid) {
data->vrm = vid_which_vrm();
ret = sysfs_create_group(&client->dev.kobj, &vid_attr_group);
if (ret)
goto eremove;
data->groups[group_num] = &vid_attr_group;
}
data->hwmon_dev = hwmon_device_register(&client->dev);
if (IS_ERR(data->hwmon_dev)) {
ret = PTR_ERR(data->hwmon_dev);
goto eremove;
/* register device with all the acquired attributes */
hwmon_dev = devm_hwmon_device_register_with_groups(&client->dev,
client->name, data,
data->groups);
if (IS_ERR(hwmon_dev)) {
ret = PTR_ERR(hwmon_dev);
return ret;
}
dev_info(&client->dev, "%s device, revision %d\n",
......@@ -1657,21 +1627,7 @@ static int adt7475_probe(struct i2c_client *client,
/* Limits and settings, should never change update more than once */
ret = adt7475_update_limits(client);
if (ret)
goto eremove;
return 0;
eremove:
adt7475_remove_files(client, data);
return ret;
}
static int adt7475_remove(struct i2c_client *client)
{
struct adt7475_data *data = i2c_get_clientdata(client);
hwmon_device_unregister(data->hwmon_dev);
adt7475_remove_files(client, data);
return ret;
return 0;
}
......@@ -1683,7 +1639,6 @@ static struct i2c_driver adt7475_driver = {
.of_match_table = of_match_ptr(adt7475_of_match),
},
.probe = adt7475_probe,
.remove = adt7475_remove,
.id_table = adt7475_id,
.detect = adt7475_detect,
.address_list = normal_i2c,
......@@ -1757,8 +1712,8 @@ static void adt7475_read_pwm(struct i2c_client *client, int index)
static int adt7475_update_measure(struct device *dev)
{
struct i2c_client *client = to_i2c_client(dev);
struct adt7475_data *data = i2c_get_clientdata(client);
struct adt7475_data *data = dev_get_drvdata(dev);
struct i2c_client *client = data->client;
u16 ext;
int i;
int ret;
......@@ -1854,8 +1809,7 @@ static int adt7475_update_measure(struct device *dev)
static struct adt7475_data *adt7475_update_device(struct device *dev)
{
struct i2c_client *client = to_i2c_client(dev);
struct adt7475_data *data = i2c_get_clientdata(client);
struct adt7475_data *data = dev_get_drvdata(dev);
int ret;
mutex_lock(&data->lock);
......
// SPDX-License-Identifier: GPL-2.0
/*
* Synaptics AS370 SoC Hardware Monitoring Driver
*
* Copyright (C) 2018 Synaptics Incorporated
* Author: Jisheng Zhang <jszhang@kernel.org>
*/
#include <linux/bitops.h>
#include <linux/hwmon.h>
#include <linux/init.h>
#include <linux/io.h>
#include <linux/module.h>
#include <linux/of_device.h>
#define CTRL 0x0
#define PD BIT(0)
#define EN BIT(1)
#define T_SEL BIT(2)
#define V_SEL BIT(3)
#define NMOS_SEL BIT(8)
#define PMOS_SEL BIT(9)
#define STS 0x4
#define BN_MASK GENMASK(11, 0)
#define EOC BIT(12)
struct as370_hwmon {
void __iomem *base;
};
static void init_pvt(struct as370_hwmon *hwmon)
{
u32 val;
void __iomem *addr = hwmon->base + CTRL;
val = PD;
writel_relaxed(val, addr);
val |= T_SEL;
writel_relaxed(val, addr);
val |= EN;
writel_relaxed(val, addr);
val &= ~PD;
writel_relaxed(val, addr);
}
static int as370_hwmon_read(struct device *dev, enum hwmon_sensor_types type,
u32 attr, int channel, long *temp)
{
int val;
struct as370_hwmon *hwmon = dev_get_drvdata(dev);
switch (attr) {
case hwmon_temp_input:
val = readl_relaxed(hwmon->base + STS) & BN_MASK;
*temp = DIV_ROUND_CLOSEST(val * 251802, 4096) - 85525;
break;
default:
return -EOPNOTSUPP;
}
return 0;
}
static umode_t
as370_hwmon_is_visible(const void *data, enum hwmon_sensor_types type,
u32 attr, int channel)
{
if (type != hwmon_temp)
return 0;
switch (attr) {
case hwmon_temp_input:
return 0444;
default:
return 0;
}
}
static const u32 as370_hwmon_temp_config[] = {
HWMON_T_INPUT,
0
};
static const struct hwmon_channel_info as370_hwmon_temp = {
.type = hwmon_temp,
.config = as370_hwmon_temp_config,
};
static const struct hwmon_channel_info *as370_hwmon_info[] = {
&as370_hwmon_temp,
NULL
};
static const struct hwmon_ops as370_hwmon_ops = {
.is_visible = as370_hwmon_is_visible,
.read = as370_hwmon_read,
};
static const struct hwmon_chip_info as370_chip_info = {
.ops = &as370_hwmon_ops,
.info = as370_hwmon_info,
};
static int as370_hwmon_probe(struct platform_device *pdev)
{
struct device *hwmon_dev;
struct as370_hwmon *hwmon;
struct device *dev = &pdev->dev;
hwmon = devm_kzalloc(dev, sizeof(*hwmon), GFP_KERNEL);
if (!hwmon)
return -ENOMEM;
hwmon->base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(hwmon->base))
return PTR_ERR(hwmon->base);
init_pvt(hwmon);
hwmon_dev = devm_hwmon_device_register_with_info(dev,
"as370",
hwmon,
&as370_chip_info,
NULL);
return PTR_ERR_OR_ZERO(hwmon_dev);
}
static const struct of_device_id as370_hwmon_match[] = {
{ .compatible = "syna,as370-hwmon" },
{},
};
MODULE_DEVICE_TABLE(of, as370_hwmon_match);
static struct platform_driver as370_hwmon_driver = {
.probe = as370_hwmon_probe,
.driver = {
.name = "as370-hwmon",
.of_match_table = as370_hwmon_match,
},
};
module_platform_driver(as370_hwmon_driver);
MODULE_AUTHOR("Jisheng Zhang<jszhang@kernel.org>");
MODULE_DESCRIPTION("Synaptics AS370 SoC hardware monitor");
MODULE_LICENSE("GPL v2");
......@@ -706,21 +706,21 @@ static int asb100_detect_subclients(struct i2c_client *client)
goto ERROR_SC_2;
}
data->lm75[0] = i2c_new_dummy(adapter, sc_addr[0]);
if (!data->lm75[0]) {
data->lm75[0] = i2c_new_dummy_device(adapter, sc_addr[0]);
if (IS_ERR(data->lm75[0])) {
dev_err(&client->dev,
"subclient %d registration at address 0x%x failed.\n",
1, sc_addr[0]);
err = -ENOMEM;
err = PTR_ERR(data->lm75[0]);
goto ERROR_SC_2;
}
data->lm75[1] = i2c_new_dummy(adapter, sc_addr[1]);
if (!data->lm75[1]) {
data->lm75[1] = i2c_new_dummy_device(adapter, sc_addr[1]);
if (IS_ERR(data->lm75[1])) {
dev_err(&client->dev,
"subclient %d registration at address 0x%x failed.\n",
2, sc_addr[1]);
err = -ENOMEM;
err = PTR_ERR(data->lm75[1]);
goto ERROR_SC_3;
}
......
......@@ -736,7 +736,7 @@ static int __init coretemp_init(void)
err = platform_driver_register(&coretemp_driver);
if (err)
return err;
goto outzone;
err = cpuhp_setup_state(CPUHP_AP_ONLINE_DYN, "hwmon/coretemp:online",
coretemp_cpu_online, coretemp_cpu_offline);
......@@ -747,6 +747,7 @@ static int __init coretemp_init(void)
outdrv:
platform_driver_unregister(&coretemp_driver);
outzone:
kfree(zone_devices);
return err;
}
......
......@@ -44,12 +44,20 @@ static ssize_t iio_hwmon_read_val(struct device *dev,
int ret;
struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr);
struct iio_hwmon_state *state = dev_get_drvdata(dev);
struct iio_channel *chan = &state->channels[sattr->index];
enum iio_chan_type type;
ret = iio_read_channel_processed(chan, &result);
if (ret < 0)
return ret;
ret = iio_read_channel_processed(&state->channels[sattr->index],
&result);
ret = iio_get_channel_type(chan, &type);
if (ret < 0)
return ret;
if (type == IIO_POWER)
result *= 1000; /* mili-Watts to micro-Watts conversion */
return sprintf(buf, "%d\n", result);
}
......@@ -59,7 +67,7 @@ static int iio_hwmon_probe(struct platform_device *pdev)
struct iio_hwmon_state *st;
struct sensor_device_attribute *a;
int ret, i;
int in_i = 1, temp_i = 1, curr_i = 1, humidity_i = 1;
int in_i = 1, temp_i = 1, curr_i = 1, humidity_i = 1, power_i = 1;
enum iio_chan_type type;
struct iio_channel *channels;
struct device *hwmon_dev;
......@@ -114,6 +122,10 @@ static int iio_hwmon_probe(struct platform_device *pdev)
n = curr_i++;
prefix = "curr";
break;
case IIO_POWER:
n = power_i++;
prefix = "power";
break;
case IIO_HUMIDITYRELATIVE:
n = humidity_i++;
prefix = "humidity";
......
......@@ -349,6 +349,7 @@ static const struct pci_device_id k10temp_id_table[] = {
{ PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_17H_DF_F3) },
{ PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_17H_M10H_DF_F3) },
{ PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_17H_M30H_DF_F3) },
{ PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_17H_M70H_DF_F3) },
{ PCI_VDEVICE(HYGON, PCI_DEVICE_ID_AMD_17H_DF_F3) },
{}
};
......
......@@ -10,10 +10,8 @@
#include <linux/module.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/jiffies.h>
#include <linux/pci.h>
#include <linux/hwmon.h>
#include <linux/hwmon-sysfs.h>
#include <linux/err.h>
#include <linux/mutex.h>
#include <asm/processor.h>
......@@ -24,108 +22,18 @@
#define SEL_CORE 0x04
struct k8temp_data {
struct device *hwmon_dev;
struct mutex update_lock;
const char *name;
char valid; /* zero until following fields are valid */
unsigned long last_updated; /* in jiffies */
/* registers values */
u8 sensorsp; /* sensor presence bits - SEL_CORE, SEL_PLACE */
u32 temp[2][2]; /* core, place */
u8 swap_core_select; /* meaning of SEL_CORE is inverted */
u32 temp_offset;
};
static struct k8temp_data *k8temp_update_device(struct device *dev)
{
struct k8temp_data *data = dev_get_drvdata(dev);
struct pci_dev *pdev = to_pci_dev(dev);
u8 tmp;
mutex_lock(&data->update_lock);
if (!data->valid
|| time_after(jiffies, data->last_updated + HZ)) {
pci_read_config_byte(pdev, REG_TEMP, &tmp);
tmp &= ~(SEL_PLACE | SEL_CORE); /* Select sensor 0, core0 */
pci_write_config_byte(pdev, REG_TEMP, tmp);
pci_read_config_dword(pdev, REG_TEMP, &data->temp[0][0]);
if (data->sensorsp & SEL_PLACE) {
tmp |= SEL_PLACE; /* Select sensor 1, core0 */
pci_write_config_byte(pdev, REG_TEMP, tmp);
pci_read_config_dword(pdev, REG_TEMP,
&data->temp[0][1]);
}
if (data->sensorsp & SEL_CORE) {
tmp &= ~SEL_PLACE; /* Select sensor 0, core1 */
tmp |= SEL_CORE;
pci_write_config_byte(pdev, REG_TEMP, tmp);
pci_read_config_dword(pdev, REG_TEMP,
&data->temp[1][0]);
if (data->sensorsp & SEL_PLACE) {
tmp |= SEL_PLACE; /* Select sensor 1, core1 */
pci_write_config_byte(pdev, REG_TEMP, tmp);
pci_read_config_dword(pdev, REG_TEMP,
&data->temp[1][1]);
}
}
data->last_updated = jiffies;
data->valid = 1;
}
mutex_unlock(&data->update_lock);
return data;
}
/*
* Sysfs stuff
*/
static ssize_t name_show(struct device *dev, struct device_attribute
*devattr, char *buf)
{
struct k8temp_data *data = dev_get_drvdata(dev);
return sprintf(buf, "%s\n", data->name);
}
static ssize_t temp_show(struct device *dev, struct device_attribute *devattr,
char *buf)
{
struct sensor_device_attribute_2 *attr =
to_sensor_dev_attr_2(devattr);
int core = attr->nr;
int place = attr->index;
int temp;
struct k8temp_data *data = k8temp_update_device(dev);
if (data->swap_core_select && (data->sensorsp & SEL_CORE))
core = core ? 0 : 1;
temp = TEMP_FROM_REG(data->temp[core][place]) + data->temp_offset;
return sprintf(buf, "%d\n", temp);
}
/* core, place */
static SENSOR_DEVICE_ATTR_2_RO(temp1_input, temp, 0, 0);
static SENSOR_DEVICE_ATTR_2_RO(temp2_input, temp, 0, 1);
static SENSOR_DEVICE_ATTR_2_RO(temp3_input, temp, 1, 0);
static SENSOR_DEVICE_ATTR_2_RO(temp4_input, temp, 1, 1);
static DEVICE_ATTR_RO(name);
static const struct pci_device_id k8temp_ids[] = {
{ PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_K8_NB_MISC) },
{ 0 },
};
MODULE_DEVICE_TABLE(pci, k8temp_ids);
static int is_rev_g_desktop(u8 model)
......@@ -159,14 +67,76 @@ static int is_rev_g_desktop(u8 model)
return 1;
}
static umode_t
k8temp_is_visible(const void *drvdata, enum hwmon_sensor_types type,
u32 attr, int channel)
{
const struct k8temp_data *data = drvdata;
if ((channel & 1) && !(data->sensorsp & SEL_PLACE))
return 0;
if ((channel & 2) && !(data->sensorsp & SEL_CORE))
return 0;
return 0444;
}
static int
k8temp_read(struct device *dev, enum hwmon_sensor_types type,
u32 attr, int channel, long *val)
{
struct k8temp_data *data = dev_get_drvdata(dev);
struct pci_dev *pdev = to_pci_dev(dev->parent);
int core, place;
u32 temp;
u8 tmp;
core = (channel >> 1) & 1;
place = channel & 1;
core ^= data->swap_core_select;
mutex_lock(&data->update_lock);
pci_read_config_byte(pdev, REG_TEMP, &tmp);
tmp &= ~(SEL_PLACE | SEL_CORE);
if (core)
tmp |= SEL_CORE;
if (place)
tmp |= SEL_PLACE;
pci_write_config_byte(pdev, REG_TEMP, tmp);
pci_read_config_dword(pdev, REG_TEMP, &temp);
mutex_unlock(&data->update_lock);
*val = TEMP_FROM_REG(temp) + data->temp_offset;
return 0;
}
static const struct hwmon_ops k8temp_ops = {
.is_visible = k8temp_is_visible,
.read = k8temp_read,
};
static const struct hwmon_channel_info *k8temp_info[] = {
HWMON_CHANNEL_INFO(temp,
HWMON_T_INPUT, HWMON_T_INPUT, HWMON_T_INPUT, HWMON_T_INPUT),
NULL
};
static const struct hwmon_chip_info k8temp_chip_info = {
.ops = &k8temp_ops,
.info = k8temp_info,
};
static int k8temp_probe(struct pci_dev *pdev,
const struct pci_device_id *id)
{
int err;
u8 scfg;
u32 temp;
u8 model, stepping;
struct k8temp_data *data;
struct device *hwmon_dev;
data = devm_kzalloc(&pdev->dev, sizeof(struct k8temp_data), GFP_KERNEL);
if (!data)
......@@ -231,86 +201,21 @@ static int k8temp_probe(struct pci_dev *pdev,
data->sensorsp &= ~SEL_CORE;
}
data->name = "k8temp";
mutex_init(&data->update_lock);
pci_set_drvdata(pdev, data);
/* Register sysfs hooks */
err = device_create_file(&pdev->dev,
&sensor_dev_attr_temp1_input.dev_attr);
if (err)
goto exit_remove;
/* sensor can be changed and reports something */
if (data->sensorsp & SEL_PLACE) {
err = device_create_file(&pdev->dev,
&sensor_dev_attr_temp2_input.dev_attr);
if (err)
goto exit_remove;
}
/* core can be changed and reports something */
if (data->sensorsp & SEL_CORE) {
err = device_create_file(&pdev->dev,
&sensor_dev_attr_temp3_input.dev_attr);
if (err)
goto exit_remove;
if (data->sensorsp & SEL_PLACE) {
err = device_create_file(&pdev->dev,
&sensor_dev_attr_temp4_input.
dev_attr);
if (err)
goto exit_remove;
}
}
err = device_create_file(&pdev->dev, &dev_attr_name);
if (err)
goto exit_remove;
data->hwmon_dev = hwmon_device_register(&pdev->dev);
hwmon_dev = devm_hwmon_device_register_with_info(&pdev->dev,
"k8temp",
data,
&k8temp_chip_info,
NULL);
if (IS_ERR(data->hwmon_dev)) {
err = PTR_ERR(data->hwmon_dev);
goto exit_remove;
}
return 0;
exit_remove:
device_remove_file(&pdev->dev,
&sensor_dev_attr_temp1_input.dev_attr);
device_remove_file(&pdev->dev,
&sensor_dev_attr_temp2_input.dev_attr);
device_remove_file(&pdev->dev,
&sensor_dev_attr_temp3_input.dev_attr);
device_remove_file(&pdev->dev,
&sensor_dev_attr_temp4_input.dev_attr);
device_remove_file(&pdev->dev, &dev_attr_name);
return err;
}
static void k8temp_remove(struct pci_dev *pdev)
{
struct k8temp_data *data = pci_get_drvdata(pdev);
hwmon_device_unregister(data->hwmon_dev);
device_remove_file(&pdev->dev,
&sensor_dev_attr_temp1_input.dev_attr);
device_remove_file(&pdev->dev,
&sensor_dev_attr_temp2_input.dev_attr);
device_remove_file(&pdev->dev,
&sensor_dev_attr_temp3_input.dev_attr);
device_remove_file(&pdev->dev,
&sensor_dev_attr_temp4_input.dev_attr);
device_remove_file(&pdev->dev, &dev_attr_name);
return PTR_ERR_OR_ZERO(hwmon_dev);
}
static struct pci_driver k8temp_driver = {
.name = "k8temp",
.id_table = k8temp_ids,
.probe = k8temp_probe,
.remove = k8temp_remove,
};
module_pci_driver(k8temp_driver);
......
......@@ -16,9 +16,9 @@
#include <linux/of_device.h>
#include <linux/of.h>
#include <linux/regmap.h>
#include <linux/util_macros.h>
#include "lm75.h"
/*
* This driver handles the LM75 and compatible digital temperature sensors.
*/
......@@ -36,6 +36,7 @@ enum lm75_type { /* keep sorted in alphabetical order */
max6626,
max31725,
mcp980x,
pct2075,
stds75,
stlm75,
tcn75,
......@@ -50,6 +51,41 @@ enum lm75_type { /* keep sorted in alphabetical order */
tmp75c,
};
/**
* struct lm75_params - lm75 configuration parameters.
* @set_mask: Bits to set in configuration register when configuring
* the chip.
* @clr_mask: Bits to clear in configuration register when configuring
* the chip.
* @default_resolution: Default number of bits to represent the temperature
* value.
* @resolution_limits: Limit register resolution. Optional. Should be set if
* the resolution of limit registers does not match the
* resolution of the temperature register.
* @resolutions: List of resolutions associated with sample times.
* Optional. Should be set if num_sample_times is larger
* than 1, and if the resolution changes with sample times.
* If set, number of entries must match num_sample_times.
* @default_sample_time:Sample time to be set by default.
* @num_sample_times: Number of possible sample times to be set. Optional.
* Should be set if the number of sample times is larger
* than one.
* @sample_times: All the possible sample times to be set. Mandatory if
* num_sample_times is larger than 1. If set, number of
* entries must match num_sample_times.
*/
struct lm75_params {
u8 set_mask;
u8 clr_mask;
u8 default_resolution;
u8 resolution_limits;
const u8 *resolutions;
unsigned int default_sample_time;
u8 num_sample_times;
const unsigned int *sample_times;
};
/* Addresses scanned */
static const unsigned short normal_i2c[] = { 0x48, 0x49, 0x4a, 0x4b, 0x4c,
0x4d, 0x4e, 0x4f, I2C_CLIENT_END };
......@@ -59,24 +95,231 @@ static const unsigned short normal_i2c[] = { 0x48, 0x49, 0x4a, 0x4b, 0x4c,
#define LM75_REG_CONF 0x01
#define LM75_REG_HYST 0x02
#define LM75_REG_MAX 0x03
#define PCT2075_REG_IDLE 0x04
/* Each client has this additional data */
struct lm75_data {
struct i2c_client *client;
struct regmap *regmap;
u8 orig_conf;
u8 resolution; /* In bits, between 9 and 16 */
u8 resolution_limits;
unsigned int sample_time; /* In ms */
struct i2c_client *client;
struct regmap *regmap;
u8 orig_conf;
u8 current_conf;
u8 resolution; /* In bits, 9 to 16 */
unsigned int sample_time; /* In ms */
enum lm75_type kind;
const struct lm75_params *params;
};
/*-----------------------------------------------------------------------*/
static const u8 lm75_sample_set_masks[] = { 0 << 5, 1 << 5, 2 << 5, 3 << 5 };
#define LM75_SAMPLE_CLEAR_MASK (3 << 5)
/* The structure below stores the configuration values of the supported devices.
* In case of being supported multiple configurations, the default one must
* always be the first element of the array
*/
static const struct lm75_params device_params[] = {
[adt75] = {
.clr_mask = 1 << 5, /* not one-shot mode */
.default_resolution = 12,
.default_sample_time = MSEC_PER_SEC / 10,
},
[ds1775] = {
.clr_mask = 3 << 5,
.set_mask = 2 << 5, /* 11-bit mode */
.default_resolution = 11,
.default_sample_time = 500,
.num_sample_times = 4,
.sample_times = (unsigned int []){ 125, 250, 500, 1000 },
.resolutions = (u8 []) {9, 10, 11, 12 },
},
[ds75] = {
.clr_mask = 3 << 5,
.set_mask = 2 << 5, /* 11-bit mode */
.default_resolution = 11,
.default_sample_time = 600,
.num_sample_times = 4,
.sample_times = (unsigned int []){ 150, 300, 600, 1200 },
.resolutions = (u8 []) {9, 10, 11, 12 },
},
[stds75] = {
.clr_mask = 3 << 5,
.set_mask = 2 << 5, /* 11-bit mode */
.default_resolution = 11,
.default_sample_time = 600,
.num_sample_times = 4,
.sample_times = (unsigned int []){ 150, 300, 600, 1200 },
.resolutions = (u8 []) {9, 10, 11, 12 },
},
[stlm75] = {
.default_resolution = 9,
.default_sample_time = MSEC_PER_SEC / 6,
},
[ds7505] = {
.set_mask = 3 << 5, /* 12-bit mode*/
.default_resolution = 12,
.default_sample_time = 200,
.num_sample_times = 4,
.sample_times = (unsigned int []){ 25, 50, 100, 200 },
.resolutions = (u8 []) {9, 10, 11, 12 },
},
[g751] = {
.default_resolution = 9,
.default_sample_time = MSEC_PER_SEC / 10,
},
[lm75] = {
.default_resolution = 9,
.default_sample_time = MSEC_PER_SEC / 10,
},
[lm75a] = {
.default_resolution = 9,
.default_sample_time = MSEC_PER_SEC / 10,
},
[lm75b] = {
.default_resolution = 11,
.default_sample_time = MSEC_PER_SEC / 10,
},
[max6625] = {
.default_resolution = 9,
.default_sample_time = MSEC_PER_SEC / 7,
},
[max6626] = {
.default_resolution = 12,
.default_sample_time = MSEC_PER_SEC / 7,
.resolution_limits = 9,
},
[max31725] = {
.default_resolution = 16,
.default_sample_time = MSEC_PER_SEC / 20,
},
[tcn75] = {
.default_resolution = 9,
.default_sample_time = MSEC_PER_SEC / 18,
},
[pct2075] = {
.default_resolution = 11,
.default_sample_time = MSEC_PER_SEC / 10,
.num_sample_times = 31,
.sample_times = (unsigned int []){ 100, 200, 300, 400, 500, 600,
700, 800, 900, 1000, 1100, 1200, 1300, 1400, 1500, 1600, 1700,
1800, 1900, 2000, 2100, 2200, 2300, 2400, 2500, 2600, 2700,
2800, 2900, 3000, 3100 },
},
[mcp980x] = {
.set_mask = 3 << 5, /* 12-bit mode */
.clr_mask = 1 << 7, /* not one-shot mode */
.default_resolution = 12,
.resolution_limits = 9,
.default_sample_time = 240,
.num_sample_times = 4,
.sample_times = (unsigned int []){ 30, 60, 120, 240 },
.resolutions = (u8 []) {9, 10, 11, 12 },
},
[tmp100] = {
.set_mask = 3 << 5, /* 12-bit mode */
.clr_mask = 1 << 7, /* not one-shot mode */
.default_resolution = 12,
.default_sample_time = 320,
.num_sample_times = 4,
.sample_times = (unsigned int []){ 40, 80, 160, 320 },
.resolutions = (u8 []) {9, 10, 11, 12 },
},
[tmp101] = {
.set_mask = 3 << 5, /* 12-bit mode */
.clr_mask = 1 << 7, /* not one-shot mode */
.default_resolution = 12,
.default_sample_time = 320,
.num_sample_times = 4,
.sample_times = (unsigned int []){ 40, 80, 160, 320 },
.resolutions = (u8 []) {9, 10, 11, 12 },
},
[tmp105] = {
.set_mask = 3 << 5, /* 12-bit mode */
.clr_mask = 1 << 7, /* not one-shot mode*/
.default_resolution = 12,
.default_sample_time = 220,
.num_sample_times = 4,
.sample_times = (unsigned int []){ 28, 55, 110, 220 },
.resolutions = (u8 []) {9, 10, 11, 12 },
},
[tmp112] = {
.set_mask = 3 << 5, /* 8 samples / second */
.clr_mask = 1 << 7, /* no one-shot mode*/
.default_resolution = 12,
.default_sample_time = 125,
.num_sample_times = 4,
.sample_times = (unsigned int []){ 125, 250, 1000, 4000 },
},
[tmp175] = {
.set_mask = 3 << 5, /* 12-bit mode */
.clr_mask = 1 << 7, /* not one-shot mode*/
.default_resolution = 12,
.default_sample_time = 220,
.num_sample_times = 4,
.sample_times = (unsigned int []){ 28, 55, 110, 220 },
.resolutions = (u8 []) {9, 10, 11, 12 },
},
[tmp275] = {
.set_mask = 3 << 5, /* 12-bit mode */
.clr_mask = 1 << 7, /* not one-shot mode*/
.default_resolution = 12,
.default_sample_time = 220,
.num_sample_times = 4,
.sample_times = (unsigned int []){ 28, 55, 110, 220 },
.resolutions = (u8 []) {9, 10, 11, 12 },
},
[tmp75] = {
.set_mask = 3 << 5, /* 12-bit mode */
.clr_mask = 1 << 7, /* not one-shot mode*/
.default_resolution = 12,
.default_sample_time = 220,
.num_sample_times = 4,
.sample_times = (unsigned int []){ 28, 55, 110, 220 },
.resolutions = (u8 []) {9, 10, 11, 12 },
},
[tmp75b] = { /* not one-shot mode, Conversion rate 37Hz */
.clr_mask = 1 << 7 | 3 << 5,
.default_resolution = 12,
.default_sample_time = MSEC_PER_SEC / 37,
.sample_times = (unsigned int []){ MSEC_PER_SEC / 37,
MSEC_PER_SEC / 18,
MSEC_PER_SEC / 9, MSEC_PER_SEC / 4 },
.num_sample_times = 4,
},
[tmp75c] = {
.clr_mask = 1 << 5, /*not one-shot mode*/
.default_resolution = 12,
.default_sample_time = MSEC_PER_SEC / 12,
}
};
static inline long lm75_reg_to_mc(s16 temp, u8 resolution)
{
return ((temp >> (16 - resolution)) * 1000) >> (resolution - 8);
}
static int lm75_write_config(struct lm75_data *data, u8 set_mask,
u8 clr_mask)
{
u8 value;
clr_mask |= LM75_SHUTDOWN;
value = data->current_conf & ~clr_mask;
value |= set_mask;
if (data->current_conf != value) {
s32 err;
err = i2c_smbus_write_byte_data(data->client, LM75_REG_CONF,
value);
if (err)
return err;
data->current_conf = value;
}
return 0;
}
static int lm75_read(struct device *dev, enum hwmon_sensor_types type,
u32 attr, int channel, long *val)
{
......@@ -120,16 +363,12 @@ static int lm75_read(struct device *dev, enum hwmon_sensor_types type,
return 0;
}
static int lm75_write(struct device *dev, enum hwmon_sensor_types type,
u32 attr, int channel, long temp)
static int lm75_write_temp(struct device *dev, u32 attr, long temp)
{
struct lm75_data *data = dev_get_drvdata(dev);
u8 resolution;
int reg;
if (type != hwmon_temp)
return -EINVAL;
switch (attr) {
case hwmon_temp_max:
reg = LM75_REG_MAX;
......@@ -145,8 +384,8 @@ static int lm75_write(struct device *dev, enum hwmon_sensor_types type,
* Resolution of limit registers is assumed to be the same as the
* temperature input register resolution unless given explicitly.
*/
if (data->resolution_limits)
resolution = data->resolution_limits;
if (data->params->resolution_limits)
resolution = data->params->resolution_limits;
else
resolution = data->resolution;
......@@ -154,16 +393,88 @@ static int lm75_write(struct device *dev, enum hwmon_sensor_types type,
temp = DIV_ROUND_CLOSEST(temp << (resolution - 8),
1000) << (16 - resolution);
return regmap_write(data->regmap, reg, temp);
return regmap_write(data->regmap, reg, (u16)temp);
}
static int lm75_update_interval(struct device *dev, long val)
{
struct lm75_data *data = dev_get_drvdata(dev);
unsigned int reg;
u8 index;
s32 err;
index = find_closest(val, data->params->sample_times,
(int)data->params->num_sample_times);
switch (data->kind) {
default:
err = lm75_write_config(data, lm75_sample_set_masks[index],
LM75_SAMPLE_CLEAR_MASK);
if (err)
return err;
data->sample_time = data->params->sample_times[index];
if (data->params->resolutions)
data->resolution = data->params->resolutions[index];
break;
case tmp112:
err = regmap_read(data->regmap, LM75_REG_CONF, &reg);
if (err < 0)
return err;
reg &= ~0x00c0;
reg |= (3 - index) << 6;
err = regmap_write(data->regmap, LM75_REG_CONF, reg);
if (err < 0)
return err;
data->sample_time = data->params->sample_times[index];
break;
case pct2075:
err = i2c_smbus_write_byte_data(data->client, PCT2075_REG_IDLE,
index + 1);
if (err)
return err;
data->sample_time = data->params->sample_times[index];
break;
}
return 0;
}
static int lm75_write_chip(struct device *dev, u32 attr, long val)
{
switch (attr) {
case hwmon_chip_update_interval:
return lm75_update_interval(dev, val);
default:
return -EINVAL;
}
return 0;
}
static int lm75_write(struct device *dev, enum hwmon_sensor_types type,
u32 attr, int channel, long val)
{
switch (type) {
case hwmon_chip:
return lm75_write_chip(dev, attr, val);
case hwmon_temp:
return lm75_write_temp(dev, attr, val);
default:
return -EINVAL;
}
return 0;
}
static umode_t lm75_is_visible(const void *data, enum hwmon_sensor_types type,
u32 attr, int channel)
{
const struct lm75_data *config_data = data;
switch (type) {
case hwmon_chip:
switch (attr) {
case hwmon_chip_update_interval:
if (config_data->params->num_sample_times > 1)
return 0644;
return 0444;
}
break;
......@@ -208,13 +519,13 @@ static bool lm75_is_writeable_reg(struct device *dev, unsigned int reg)
static bool lm75_is_volatile_reg(struct device *dev, unsigned int reg)
{
return reg == LM75_REG_TEMP;
return reg == LM75_REG_TEMP || reg == LM75_REG_CONF;
}
static const struct regmap_config lm75_regmap_config = {
.reg_bits = 8,
.val_bits = 16,
.max_register = LM75_REG_MAX,
.max_register = PCT2075_REG_IDLE,
.writeable_reg = lm75_is_writeable_reg,
.volatile_reg = lm75_is_volatile_reg,
.val_format_endian = REGMAP_ENDIAN_BIG,
......@@ -238,8 +549,6 @@ lm75_probe(struct i2c_client *client, const struct i2c_device_id *id)
struct device *hwmon_dev;
struct lm75_data *data;
int status, err;
u8 set_mask, clr_mask;
int new;
enum lm75_type kind;
if (client->dev.of_node)
......@@ -256,6 +565,7 @@ lm75_probe(struct i2c_client *client, const struct i2c_device_id *id)
return -ENOMEM;
data->client = client;
data->kind = kind;
data->regmap = devm_regmap_init_i2c(client, &lm75_regmap_config);
if (IS_ERR(data->regmap))
......@@ -264,113 +574,30 @@ lm75_probe(struct i2c_client *client, const struct i2c_device_id *id)
/* Set to LM75 resolution (9 bits, 1/2 degree C) and range.
* Then tweak to be more precise when appropriate.
*/
set_mask = 0;
clr_mask = LM75_SHUTDOWN; /* continuous conversions */
switch (kind) {
case adt75:
clr_mask |= 1 << 5; /* not one-shot mode */
data->resolution = 12;
data->sample_time = MSEC_PER_SEC / 8;
break;
case ds1775:
case ds75:
case stds75:
clr_mask |= 3 << 5;
set_mask |= 2 << 5; /* 11-bit mode */
data->resolution = 11;
data->sample_time = MSEC_PER_SEC;
break;
case stlm75:
data->resolution = 9;
data->sample_time = MSEC_PER_SEC / 5;
break;
case ds7505:
set_mask |= 3 << 5; /* 12-bit mode */
data->resolution = 12;
data->sample_time = MSEC_PER_SEC / 4;
break;
case g751:
case lm75:
case lm75a:
data->resolution = 9;
data->sample_time = MSEC_PER_SEC / 2;
break;
case lm75b:
data->resolution = 11;
data->sample_time = MSEC_PER_SEC / 4;
break;
case max6625:
data->resolution = 9;
data->sample_time = MSEC_PER_SEC / 4;
break;
case max6626:
data->resolution = 12;
data->resolution_limits = 9;
data->sample_time = MSEC_PER_SEC / 4;
break;
case max31725:
data->resolution = 16;
data->sample_time = MSEC_PER_SEC / 8;
break;
case tcn75:
data->resolution = 9;
data->sample_time = MSEC_PER_SEC / 8;
break;
case mcp980x:
data->resolution_limits = 9;
/* fall through */
case tmp100:
case tmp101:
set_mask |= 3 << 5; /* 12-bit mode */
data->resolution = 12;
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 = MSEC_PER_SEC / 4;
break;
case tmp105:
case tmp175:
case tmp275:
case tmp75:
set_mask |= 3 << 5; /* 12-bit mode */
clr_mask |= 1 << 7; /* not one-shot mode */
data->resolution = 12;
data->sample_time = MSEC_PER_SEC / 2;
break;
case tmp75b: /* not one-shot mode, Conversion rate 37Hz */
clr_mask |= 1 << 7 | 0x3 << 5;
data->resolution = 12;
data->sample_time = MSEC_PER_SEC / 37;
break;
case tmp75c:
clr_mask |= 1 << 5; /* not one-shot mode */
data->resolution = 12;
data->sample_time = MSEC_PER_SEC / 4;
break;
}
/* configure as specified */
data->params = &device_params[data->kind];
/* Save default sample time and resolution*/
data->sample_time = data->params->default_sample_time;
data->resolution = data->params->default_resolution;
/* Cache original configuration */
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;
}
data->orig_conf = status;
new = status & ~clr_mask;
new |= set_mask;
if (status != new)
i2c_smbus_write_byte_data(client, LM75_REG_CONF, new);
data->current_conf = status;
err = devm_add_action_or_reset(dev, lm75_remove, data);
err = lm75_write_config(data, data->params->set_mask,
data->params->clr_mask);
if (err)
return err;
dev_dbg(dev, "Config %02x\n", new);
err = devm_add_action_or_reset(dev, lm75_remove, data);
if (err)
return err;
hwmon_dev = devm_hwmon_device_register_with_info(dev, client->name,
data, &lm75_chip_info,
......@@ -397,6 +624,7 @@ static const struct i2c_device_id lm75_ids[] = {
{ "max31725", max31725, },
{ "max31726", max31725, },
{ "mcp980x", mcp980x, },
{ "pct2075", pct2075, },
{ "stds75", stds75, },
{ "stlm75", stlm75, },
{ "tcn75", tcn75, },
......@@ -466,6 +694,10 @@ static const struct of_device_id __maybe_unused lm75_of_match[] = {
.compatible = "maxim,mcp980x",
.data = (void *)mcp980x
},
{
.compatible = "nxp,pct2075",
.data = (void *)pct2075
},
{
.compatible = "st,stds75",
.data = (void *)stds75
......
......@@ -13,7 +13,7 @@
#include <linux/i2c.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/property.h>
#define LTC2990_STATUS 0x00
#define LTC2990_CONTROL 0x01
......@@ -206,7 +206,6 @@ static int ltc2990_i2c_probe(struct i2c_client *i2c,
int ret;
struct device *hwmon_dev;
struct ltc2990_data *data;
struct device_node *of_node = i2c->dev.of_node;
if (!i2c_check_functionality(i2c->adapter, I2C_FUNC_SMBUS_BYTE_DATA |
I2C_FUNC_SMBUS_WORD_DATA))
......@@ -218,9 +217,10 @@ static int ltc2990_i2c_probe(struct i2c_client *i2c,
data->i2c = i2c;
if (of_node) {
ret = of_property_read_u32_array(of_node, "lltc,meas-mode",
data->mode, 2);
if (dev_fwnode(&i2c->dev)) {
ret = device_property_read_u32_array(&i2c->dev,
"lltc,meas-mode",
data->mode, 2);
if (ret < 0)
return ret;
......
......@@ -20,6 +20,7 @@
*
* Chip #vin #fan #pwm #temp chip IDs man ID
* nct6106d 9 3 3 6+3 0xc450 0xc1 0x5ca3
* nct6116d 9 5 5 3+3 0xd280 0xc1 0x5ca3
* nct6775f 9 4 3 6+3 0xb470 0xc1 0x5ca3
* nct6776f 9 5 3 6+3 0xc330 0xc1 0x5ca3
* nct6779d 15 5 5 2+6 0xc560 0xc1 0x5ca3
......@@ -58,12 +59,13 @@
#define USE_ALTERNATE
enum kinds { nct6106, nct6775, nct6776, nct6779, nct6791, nct6792, nct6793,
nct6795, nct6796, nct6797, nct6798 };
enum kinds { nct6106, nct6116, nct6775, nct6776, nct6779, nct6791, nct6792,
nct6793, nct6795, nct6796, nct6797, nct6798 };
/* used to set data->name = nct6775_device_names[data->sio_kind] */
static const char * const nct6775_device_names[] = {
"nct6106",
"nct6116",
"nct6775",
"nct6776",
"nct6779",
......@@ -78,6 +80,7 @@ static const char * const nct6775_device_names[] = {
static const char * const nct6775_sio_names[] __initconst = {
"NCT6106D",
"NCT6116D",
"NCT6775F",
"NCT6776D/F",
"NCT6779D",
......@@ -115,6 +118,7 @@ MODULE_PARM_DESC(fan_debounce, "Enable debouncing for fan RPM signal");
#define SIO_REG_ADDR 0x60 /* Logical device address (2 bytes) */
#define SIO_NCT6106_ID 0xc450
#define SIO_NCT6116_ID 0xd280
#define SIO_NCT6775_ID 0xb470
#define SIO_NCT6776_ID 0xc330
#define SIO_NCT6779_ID 0xc560
......@@ -825,10 +829,8 @@ static const u16 NCT6106_FAN_PULSE_SHIFT[] = { 0, 2, 4 };
static const u8 NCT6106_REG_PWM_MODE[] = { 0xf3, 0xf3, 0xf3 };
static const u8 NCT6106_PWM_MODE_MASK[] = { 0x01, 0x02, 0x04 };
static const u16 NCT6106_REG_PWM[] = { 0x119, 0x129, 0x139 };
static const u16 NCT6106_REG_PWM_READ[] = { 0x4a, 0x4b, 0x4c };
static const u16 NCT6106_REG_FAN_MODE[] = { 0x113, 0x123, 0x133 };
static const u16 NCT6106_REG_TEMP_SEL[] = { 0x110, 0x120, 0x130 };
static const u16 NCT6106_REG_TEMP_SOURCE[] = {
0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5 };
......@@ -896,6 +898,70 @@ static const u16 NCT6106_REG_TEMP_CRIT[32] = {
[12] = 0x205,
};
/* NCT6112D/NCT6114D/NCT6116D specific data */
static const u16 NCT6116_REG_FAN[] = { 0x20, 0x22, 0x24, 0x26, 0x28 };
static const u16 NCT6116_REG_FAN_MIN[] = { 0xe0, 0xe2, 0xe4, 0xe6, 0xe8 };
static const u16 NCT6116_REG_FAN_PULSES[] = { 0xf6, 0xf6, 0xf6, 0xf6, 0xf5 };
static const u16 NCT6116_FAN_PULSE_SHIFT[] = { 0, 2, 4, 6, 6 };
static const u16 NCT6116_REG_PWM[] = { 0x119, 0x129, 0x139, 0x199, 0x1a9 };
static const u16 NCT6116_REG_FAN_MODE[] = { 0x113, 0x123, 0x133, 0x193, 0x1a3 };
static const u16 NCT6116_REG_TEMP_SEL[] = { 0x110, 0x120, 0x130, 0x190, 0x1a0 };
static const u16 NCT6116_REG_TEMP_SOURCE[] = {
0xb0, 0xb1, 0xb2 };
static const u16 NCT6116_REG_CRITICAL_TEMP[] = {
0x11a, 0x12a, 0x13a, 0x19a, 0x1aa };
static const u16 NCT6116_REG_CRITICAL_TEMP_TOLERANCE[] = {
0x11b, 0x12b, 0x13b, 0x19b, 0x1ab };
static const u16 NCT6116_REG_CRITICAL_PWM_ENABLE[] = {
0x11c, 0x12c, 0x13c, 0x19c, 0x1ac };
static const u16 NCT6116_REG_CRITICAL_PWM[] = {
0x11d, 0x12d, 0x13d, 0x19d, 0x1ad };
static const u16 NCT6116_REG_FAN_STEP_UP_TIME[] = {
0x114, 0x124, 0x134, 0x194, 0x1a4 };
static const u16 NCT6116_REG_FAN_STEP_DOWN_TIME[] = {
0x115, 0x125, 0x135, 0x195, 0x1a5 };
static const u16 NCT6116_REG_FAN_STOP_OUTPUT[] = {
0x116, 0x126, 0x136, 0x196, 0x1a6 };
static const u16 NCT6116_REG_FAN_START_OUTPUT[] = {
0x117, 0x127, 0x137, 0x197, 0x1a7 };
static const u16 NCT6116_REG_FAN_STOP_TIME[] = {
0x118, 0x128, 0x138, 0x198, 0x1a8 };
static const u16 NCT6116_REG_TOLERANCE_H[] = {
0x112, 0x122, 0x132, 0x192, 0x1a2 };
static const u16 NCT6116_REG_TARGET[] = {
0x111, 0x121, 0x131, 0x191, 0x1a1 };
static const u16 NCT6116_REG_AUTO_TEMP[] = {
0x160, 0x170, 0x180, 0x1d0, 0x1e0 };
static const u16 NCT6116_REG_AUTO_PWM[] = {
0x164, 0x174, 0x184, 0x1d4, 0x1e4 };
static const s8 NCT6116_ALARM_BITS[] = {
0, 1, 2, 3, 4, 5, 7, 8, /* in0.. in7 */
9, -1, -1, -1, -1, -1, -1, /* in8..in9 */
-1, /* unused */
32, 33, 34, 35, 36, /* fan1..fan5 */
-1, -1, -1, /* unused */
16, 17, 18, -1, -1, -1, /* temp1..temp6 */
48, -1 /* intrusion0, intrusion1 */
};
static const s8 NCT6116_BEEP_BITS[] = {
0, 1, 2, 3, 4, 5, 7, 8, /* in0.. in7 */
9, 10, 11, 12, -1, -1, -1, /* in8..in14 */
32, /* global beep enable */
24, 25, 26, 27, 28, /* fan1..fan5 */
-1, -1, -1, /* unused */
16, 17, 18, -1, -1, -1, /* temp1..temp6 */
34, -1 /* intrusion0, intrusion1 */
};
static enum pwm_enable reg_to_pwm_enable(int pwm, int mode)
{
if (mode == 0 && pwm == 255)
......@@ -1294,6 +1360,11 @@ static bool is_word_sized(struct nct6775_data *data, u16 reg)
return reg == 0x20 || reg == 0x22 || reg == 0x24 ||
reg == 0xe0 || reg == 0xe2 || reg == 0xe4 ||
reg == 0x111 || reg == 0x121 || reg == 0x131;
case nct6116:
return reg == 0x20 || reg == 0x22 || reg == 0x24 ||
reg == 0x26 || reg == 0x28 || reg == 0xe0 || reg == 0xe2 ||
reg == 0xe4 || reg == 0xe6 || reg == 0xe8 || reg == 0x111 ||
reg == 0x121 || reg == 0x131 || reg == 0x191 || reg == 0x1a1;
case nct6775:
return (((reg & 0xff00) == 0x100 ||
(reg & 0xff00) == 0x200) &&
......@@ -1673,6 +1744,7 @@ static void nct6775_update_pwm_limits(struct device *dev)
data->auto_pwm[i][data->auto_pwm_num] = 0xff;
break;
case nct6106:
case nct6116:
case nct6779:
case nct6791:
case nct6792:
......@@ -3109,6 +3181,7 @@ store_auto_pwm(struct device *dev, struct device_attribute *attr,
case nct6776:
break; /* always enabled, nothing to do */
case nct6106:
case nct6116:
case nct6779:
case nct6791:
case nct6792:
......@@ -3535,6 +3608,23 @@ nct6775_check_fan_inputs(struct nct6775_data *data)
fan3pin = !(cr24 & 0x80);
pwm3pin = cr24 & 0x08;
} else if (data->kind == nct6116) {
int cr1a = superio_inb(sioreg, 0x1a);
int cr1b = superio_inb(sioreg, 0x1b);
int cr24 = superio_inb(sioreg, 0x24);
int cr2a = superio_inb(sioreg, 0x2a);
int cr2b = superio_inb(sioreg, 0x2b);
int cr2f = superio_inb(sioreg, 0x2f);
fan3pin = !(cr2b & 0x10);
fan4pin = (cr2b & 0x80) || // pin 1(2)
(!(cr2f & 0x10) && (cr1a & 0x04)); // pin 65(66)
fan5pin = (cr2b & 0x80) || // pin 126(127)
(!(cr1b & 0x03) && (cr2a & 0x02)); // pin 94(96)
pwm3pin = fan3pin && (cr24 & 0x08);
pwm4pin = fan4pin;
pwm5pin = fan5pin;
} else {
/*
* NCT6779D, NCT6791D, NCT6792D, NCT6793D, NCT6795D, NCT6796D,
......@@ -3765,7 +3855,7 @@ static int nct6775_probe(struct platform_device *pdev)
data->REG_FAN_TIME[1] = NCT6106_REG_FAN_STEP_UP_TIME;
data->REG_FAN_TIME[2] = NCT6106_REG_FAN_STEP_DOWN_TIME;
data->REG_TOLERANCE_H = NCT6106_REG_TOLERANCE_H;
data->REG_PWM[0] = NCT6106_REG_PWM;
data->REG_PWM[0] = NCT6116_REG_PWM;
data->REG_PWM[1] = NCT6106_REG_FAN_START_OUTPUT;
data->REG_PWM[2] = NCT6106_REG_FAN_STOP_OUTPUT;
data->REG_PWM[5] = NCT6106_REG_WEIGHT_DUTY_STEP;
......@@ -3784,7 +3874,7 @@ static int nct6775_probe(struct platform_device *pdev)
data->REG_CRITICAL_PWM = NCT6106_REG_CRITICAL_PWM;
data->REG_TEMP_OFFSET = NCT6106_REG_TEMP_OFFSET;
data->REG_TEMP_SOURCE = NCT6106_REG_TEMP_SOURCE;
data->REG_TEMP_SEL = NCT6106_REG_TEMP_SEL;
data->REG_TEMP_SEL = NCT6116_REG_TEMP_SEL;
data->REG_WEIGHT_TEMP_SEL = NCT6106_REG_WEIGHT_TEMP_SEL;
data->REG_WEIGHT_TEMP[0] = NCT6106_REG_WEIGHT_TEMP_STEP;
data->REG_WEIGHT_TEMP[1] = NCT6106_REG_WEIGHT_TEMP_STEP_TOL;
......@@ -3806,6 +3896,79 @@ static int nct6775_probe(struct platform_device *pdev)
reg_temp_crit_l = NCT6106_REG_TEMP_CRIT_L;
reg_temp_crit_h = NCT6106_REG_TEMP_CRIT_H;
break;
case nct6116:
data->in_num = 9;
data->pwm_num = 3;
data->auto_pwm_num = 4;
data->temp_fixed_num = 3;
data->num_temp_alarms = 3;
data->num_temp_beeps = 3;
data->fan_from_reg = fan_from_reg13;
data->fan_from_reg_min = fan_from_reg13;
data->temp_label = nct6776_temp_label;
data->temp_mask = NCT6776_TEMP_MASK;
data->virt_temp_mask = NCT6776_VIRT_TEMP_MASK;
data->REG_VBAT = NCT6106_REG_VBAT;
data->REG_DIODE = NCT6106_REG_DIODE;
data->DIODE_MASK = NCT6106_DIODE_MASK;
data->REG_VIN = NCT6106_REG_IN;
data->REG_IN_MINMAX[0] = NCT6106_REG_IN_MIN;
data->REG_IN_MINMAX[1] = NCT6106_REG_IN_MAX;
data->REG_TARGET = NCT6116_REG_TARGET;
data->REG_FAN = NCT6116_REG_FAN;
data->REG_FAN_MODE = NCT6116_REG_FAN_MODE;
data->REG_FAN_MIN = NCT6116_REG_FAN_MIN;
data->REG_FAN_PULSES = NCT6116_REG_FAN_PULSES;
data->FAN_PULSE_SHIFT = NCT6116_FAN_PULSE_SHIFT;
data->REG_FAN_TIME[0] = NCT6116_REG_FAN_STOP_TIME;
data->REG_FAN_TIME[1] = NCT6116_REG_FAN_STEP_UP_TIME;
data->REG_FAN_TIME[2] = NCT6116_REG_FAN_STEP_DOWN_TIME;
data->REG_TOLERANCE_H = NCT6116_REG_TOLERANCE_H;
data->REG_PWM[0] = NCT6116_REG_PWM;
data->REG_PWM[1] = NCT6116_REG_FAN_START_OUTPUT;
data->REG_PWM[2] = NCT6116_REG_FAN_STOP_OUTPUT;
data->REG_PWM[5] = NCT6106_REG_WEIGHT_DUTY_STEP;
data->REG_PWM[6] = NCT6106_REG_WEIGHT_DUTY_BASE;
data->REG_PWM_READ = NCT6106_REG_PWM_READ;
data->REG_PWM_MODE = NCT6106_REG_PWM_MODE;
data->PWM_MODE_MASK = NCT6106_PWM_MODE_MASK;
data->REG_AUTO_TEMP = NCT6116_REG_AUTO_TEMP;
data->REG_AUTO_PWM = NCT6116_REG_AUTO_PWM;
data->REG_CRITICAL_TEMP = NCT6116_REG_CRITICAL_TEMP;
data->REG_CRITICAL_TEMP_TOLERANCE
= NCT6116_REG_CRITICAL_TEMP_TOLERANCE;
data->REG_CRITICAL_PWM_ENABLE = NCT6116_REG_CRITICAL_PWM_ENABLE;
data->CRITICAL_PWM_ENABLE_MASK
= NCT6106_CRITICAL_PWM_ENABLE_MASK;
data->REG_CRITICAL_PWM = NCT6116_REG_CRITICAL_PWM;
data->REG_TEMP_OFFSET = NCT6106_REG_TEMP_OFFSET;
data->REG_TEMP_SOURCE = NCT6116_REG_TEMP_SOURCE;
data->REG_TEMP_SEL = NCT6116_REG_TEMP_SEL;
data->REG_WEIGHT_TEMP_SEL = NCT6106_REG_WEIGHT_TEMP_SEL;
data->REG_WEIGHT_TEMP[0] = NCT6106_REG_WEIGHT_TEMP_STEP;
data->REG_WEIGHT_TEMP[1] = NCT6106_REG_WEIGHT_TEMP_STEP_TOL;
data->REG_WEIGHT_TEMP[2] = NCT6106_REG_WEIGHT_TEMP_BASE;
data->REG_ALARM = NCT6106_REG_ALARM;
data->ALARM_BITS = NCT6116_ALARM_BITS;
data->REG_BEEP = NCT6106_REG_BEEP;
data->BEEP_BITS = NCT6116_BEEP_BITS;
reg_temp = NCT6106_REG_TEMP;
reg_temp_mon = NCT6106_REG_TEMP_MON;
num_reg_temp = ARRAY_SIZE(NCT6106_REG_TEMP);
num_reg_temp_mon = ARRAY_SIZE(NCT6106_REG_TEMP_MON);
reg_temp_over = NCT6106_REG_TEMP_OVER;
reg_temp_hyst = NCT6106_REG_TEMP_HYST;
reg_temp_config = NCT6106_REG_TEMP_CONFIG;
reg_temp_alternate = NCT6106_REG_TEMP_ALTERNATE;
reg_temp_crit = NCT6106_REG_TEMP_CRIT;
reg_temp_crit_l = NCT6106_REG_TEMP_CRIT_L;
reg_temp_crit_h = NCT6106_REG_TEMP_CRIT_H;
break;
case nct6775:
data->in_num = 9;
......@@ -4352,6 +4515,7 @@ static int nct6775_probe(struct platform_device *pdev)
data->have_vid = (cr2a & 0x60) == 0x40;
break;
case nct6106:
case nct6116:
case nct6779:
case nct6791:
case nct6792:
......@@ -4381,6 +4545,7 @@ static int nct6775_probe(struct platform_device *pdev)
NCT6775_REG_CR_FAN_DEBOUNCE);
switch (data->kind) {
case nct6106:
case nct6116:
tmp |= 0xe0;
break;
case nct6775:
......@@ -4576,6 +4741,9 @@ static int __init nct6775_find(int sioaddr, struct nct6775_sio_data *sio_data)
case SIO_NCT6106_ID:
sio_data->kind = nct6106;
break;
case SIO_NCT6116_ID:
sio_data->kind = nct6116;
break;
case SIO_NCT6775_ID:
sio_data->kind = nct6775;
break;
......
......@@ -46,10 +46,34 @@
#define DTS_T_CTRL1_REG 0x27
#define VT_ADC_MD_REG 0x2E
#define VSEN1_HV_LL_REG 0x02 /* Bank 1; 2 regs (HV/LV) per sensor */
#define VSEN1_LV_LL_REG 0x03 /* Bank 1; 2 regs (HV/LV) per sensor */
#define VSEN1_HV_HL_REG 0x00 /* Bank 1; 2 regs (HV/LV) per sensor */
#define VSEN1_LV_HL_REG 0x01 /* Bank 1; 2 regs (HV/LV) per sensor */
#define SMI_STS1_REG 0xC1 /* Bank 0; SMI Status Register */
#define SMI_STS3_REG 0xC3 /* Bank 0; SMI Status Register */
#define SMI_STS5_REG 0xC5 /* Bank 0; SMI Status Register */
#define SMI_STS7_REG 0xC7 /* Bank 0; SMI Status Register */
#define SMI_STS8_REG 0xC8 /* Bank 0; SMI Status Register */
#define VSEN1_HV_REG 0x40 /* Bank 0; 2 regs (HV/LV) per sensor */
#define TEMP_CH1_HV_REG 0x42 /* Bank 0; same as VSEN2_HV */
#define LTD_HV_REG 0x62 /* Bank 0; 2 regs in VSEN range */
#define LTD_HV_HL_REG 0x44 /* Bank 1; 1 reg for LTD */
#define LTD_LV_HL_REG 0x45 /* Bank 1; 1 reg for LTD */
#define LTD_HV_LL_REG 0x46 /* Bank 1; 1 reg for LTD */
#define LTD_LV_LL_REG 0x47 /* Bank 1; 1 reg for LTD */
#define TEMP_CH1_CH_REG 0x05 /* Bank 1; 1 reg for LTD */
#define TEMP_CH1_W_REG 0x06 /* Bank 1; 1 reg for LTD */
#define TEMP_CH1_WH_REG 0x07 /* Bank 1; 1 reg for LTD */
#define TEMP_CH1_C_REG 0x04 /* Bank 1; 1 reg per sensor */
#define DTS_T_CPU1_C_REG 0x90 /* Bank 1; 1 reg per sensor */
#define DTS_T_CPU1_CH_REG 0x91 /* Bank 1; 1 reg per sensor */
#define DTS_T_CPU1_W_REG 0x92 /* Bank 1; 1 reg per sensor */
#define DTS_T_CPU1_WH_REG 0x93 /* Bank 1; 1 reg per sensor */
#define FANIN1_HV_REG 0x80 /* Bank 0; 2 regs (HV/LV) per sensor */
#define FANIN1_HV_HL_REG 0x60 /* Bank 1; 2 regs (HV/LV) per sensor */
#define FANIN1_LV_HL_REG 0x61 /* Bank 1; 2 regs (HV/LV) per sensor */
#define T_CPU1_HV_REG 0xA0 /* Bank 0; 2 regs (HV/LV) per sensor */
#define PRTS_REG 0x03 /* Bank 2 */
......@@ -58,6 +82,8 @@
#define FANCTL1_FMR_REG 0x00 /* Bank 3; 1 reg per channel */
#define FANCTL1_OUT_REG 0x10 /* Bank 3; 1 reg per channel */
#define ENABLE_TSI BIT(1)
static const unsigned short normal_i2c[] = {
0x2d, 0x2e, I2C_CLIENT_END
};
......@@ -72,6 +98,7 @@ struct nct7904_data {
u8 fan_mode[FANCTL_MAX];
u8 enable_dts;
u8 has_dts;
u8 temp_mode; /* 0: TR mode, 1: TD mode */
};
/* Access functions */
......@@ -170,6 +197,25 @@ static int nct7904_read_fan(struct device *dev, u32 attr, int channel,
rpm = 1350000 / cnt;
*val = rpm;
return 0;
case hwmon_fan_min:
ret = nct7904_read_reg16(data, BANK_1,
FANIN1_HV_HL_REG + channel * 2);
if (ret < 0)
return ret;
cnt = ((ret & 0xff00) >> 3) | (ret & 0x1f);
if (cnt == 0x1fff)
rpm = 0;
else
rpm = 1350000 / cnt;
*val = rpm;
return 0;
case hwmon_fan_alarm:
ret = nct7904_read_reg(data, BANK_0,
SMI_STS5_REG + (channel >> 3));
if (ret < 0)
return ret;
*val = (ret >> (channel & 0x07)) & 1;
return 0;
default:
return -EOPNOTSUPP;
}
......@@ -179,8 +225,20 @@ static umode_t nct7904_fan_is_visible(const void *_data, u32 attr, int channel)
{
const struct nct7904_data *data = _data;
if (attr == hwmon_fan_input && data->fanin_mask & (1 << channel))
return 0444;
switch (attr) {
case hwmon_fan_input:
case hwmon_fan_alarm:
if (data->fanin_mask & (1 << channel))
return 0444;
break;
case hwmon_fan_min:
if (data->fanin_mask & (1 << channel))
return 0644;
break;
default:
break;
}
return 0;
}
......@@ -211,6 +269,37 @@ static int nct7904_read_in(struct device *dev, u32 attr, int channel,
volt *= 6; /* 0.006V scale */
*val = volt;
return 0;
case hwmon_in_min:
ret = nct7904_read_reg16(data, BANK_1,
VSEN1_HV_LL_REG + index * 4);
if (ret < 0)
return ret;
volt = ((ret & 0xff00) >> 5) | (ret & 0x7);
if (index < 14)
volt *= 2; /* 0.002V scale */
else
volt *= 6; /* 0.006V scale */
*val = volt;
return 0;
case hwmon_in_max:
ret = nct7904_read_reg16(data, BANK_1,
VSEN1_HV_HL_REG + index * 4);
if (ret < 0)
return ret;
volt = ((ret & 0xff00) >> 5) | (ret & 0x7);
if (index < 14)
volt *= 2; /* 0.002V scale */
else
volt *= 6; /* 0.006V scale */
*val = volt;
return 0;
case hwmon_in_alarm:
ret = nct7904_read_reg(data, BANK_0,
SMI_STS1_REG + (index >> 3));
if (ret < 0)
return ret;
*val = (ret >> (index & 0x07)) & 1;
return 0;
default:
return -EOPNOTSUPP;
}
......@@ -221,9 +310,20 @@ static umode_t nct7904_in_is_visible(const void *_data, u32 attr, int channel)
const struct nct7904_data *data = _data;
int index = nct7904_chan_to_index[channel];
if (channel > 0 && attr == hwmon_in_input &&
(data->vsen_mask & BIT(index)))
return 0444;
switch (attr) {
case hwmon_in_input:
case hwmon_in_alarm:
if (channel > 0 && (data->vsen_mask & BIT(index)))
return 0444;
break;
case hwmon_in_min:
case hwmon_in_max:
if (channel > 0 && (data->vsen_mask & BIT(index)))
return 0644;
break;
default:
break;
}
return 0;
}
......@@ -233,6 +333,7 @@ static int nct7904_read_temp(struct device *dev, u32 attr, int channel,
{
struct nct7904_data *data = dev_get_drvdata(dev);
int ret, temp;
unsigned int reg1, reg2, reg3;
switch (attr) {
case hwmon_temp_input:
......@@ -250,16 +351,106 @@ static int nct7904_read_temp(struct device *dev, u32 attr, int channel,
temp = ((ret & 0xff00) >> 5) | (ret & 0x7);
*val = sign_extend32(temp, 10) * 125;
return 0;
case hwmon_temp_alarm:
if (channel == 4) {
ret = nct7904_read_reg(data, BANK_0,
SMI_STS3_REG);
if (ret < 0)
return ret;
*val = (ret >> 1) & 1;
} else if (channel < 4) {
ret = nct7904_read_reg(data, BANK_0,
SMI_STS1_REG);
if (ret < 0)
return ret;
*val = (ret >> (((channel * 2) + 1) & 0x07)) & 1;
} else {
if ((channel - 5) < 4) {
ret = nct7904_read_reg(data, BANK_0,
SMI_STS7_REG +
((channel - 5) >> 3));
if (ret < 0)
return ret;
*val = (ret >> ((channel - 5) & 0x07)) & 1;
} else {
ret = nct7904_read_reg(data, BANK_0,
SMI_STS8_REG +
((channel - 5) >> 3));
if (ret < 0)
return ret;
*val = (ret >> (((channel - 5) & 0x07) - 4))
& 1;
}
}
return 0;
case hwmon_temp_type:
if (channel < 5) {
if ((data->tcpu_mask >> channel) & 0x01) {
if ((data->temp_mode >> channel) & 0x01)
*val = 3; /* TD */
else
*val = 4; /* TR */
} else {
*val = 0;
}
} else {
if ((data->has_dts >> (channel - 5)) & 0x01) {
if (data->enable_dts & ENABLE_TSI)
*val = 5; /* TSI */
else
*val = 6; /* PECI */
} else {
*val = 0;
}
}
return 0;
case hwmon_temp_max:
reg1 = LTD_HV_LL_REG;
reg2 = TEMP_CH1_W_REG;
reg3 = DTS_T_CPU1_W_REG;
break;
case hwmon_temp_max_hyst:
reg1 = LTD_LV_LL_REG;
reg2 = TEMP_CH1_WH_REG;
reg3 = DTS_T_CPU1_WH_REG;
break;
case hwmon_temp_crit:
reg1 = LTD_HV_HL_REG;
reg2 = TEMP_CH1_C_REG;
reg3 = DTS_T_CPU1_C_REG;
break;
case hwmon_temp_crit_hyst:
reg1 = LTD_LV_HL_REG;
reg2 = TEMP_CH1_CH_REG;
reg3 = DTS_T_CPU1_CH_REG;
break;
default:
return -EOPNOTSUPP;
}
if (channel == 4)
ret = nct7904_read_reg(data, BANK_1, reg1);
else if (channel < 5)
ret = nct7904_read_reg(data, BANK_1,
reg2 + channel * 8);
else
ret = nct7904_read_reg(data, BANK_1,
reg3 + (channel - 5) * 4);
if (ret < 0)
return ret;
*val = ret * 1000;
return 0;
}
static umode_t nct7904_temp_is_visible(const void *_data, u32 attr, int channel)
{
const struct nct7904_data *data = _data;
if (attr == hwmon_temp_input) {
switch (attr) {
case hwmon_temp_input:
case hwmon_temp_alarm:
case hwmon_temp_type:
if (channel < 5) {
if (data->tcpu_mask & BIT(channel))
return 0444;
......@@ -267,6 +458,21 @@ static umode_t nct7904_temp_is_visible(const void *_data, u32 attr, int channel)
if (data->has_dts & BIT(channel - 5))
return 0444;
}
break;
case hwmon_temp_max:
case hwmon_temp_max_hyst:
case hwmon_temp_crit:
case hwmon_temp_crit_hyst:
if (channel < 5) {
if (data->tcpu_mask & BIT(channel))
return 0644;
} else {
if (data->has_dts & BIT(channel - 5))
return 0644;
}
break;
default:
break;
}
return 0;
......@@ -297,6 +503,137 @@ static int nct7904_read_pwm(struct device *dev, u32 attr, int channel,
}
}
static int nct7904_write_temp(struct device *dev, u32 attr, int channel,
long val)
{
struct nct7904_data *data = dev_get_drvdata(dev);
int ret;
unsigned int reg1, reg2, reg3;
val = clamp_val(val / 1000, -128, 127);
switch (attr) {
case hwmon_temp_max:
reg1 = LTD_HV_LL_REG;
reg2 = TEMP_CH1_W_REG;
reg3 = DTS_T_CPU1_W_REG;
break;
case hwmon_temp_max_hyst:
reg1 = LTD_LV_LL_REG;
reg2 = TEMP_CH1_WH_REG;
reg3 = DTS_T_CPU1_WH_REG;
break;
case hwmon_temp_crit:
reg1 = LTD_HV_HL_REG;
reg2 = TEMP_CH1_C_REG;
reg3 = DTS_T_CPU1_C_REG;
break;
case hwmon_temp_crit_hyst:
reg1 = LTD_LV_HL_REG;
reg2 = TEMP_CH1_CH_REG;
reg3 = DTS_T_CPU1_CH_REG;
break;
default:
return -EOPNOTSUPP;
}
if (channel == 4)
ret = nct7904_write_reg(data, BANK_1, reg1, val);
else if (channel < 5)
ret = nct7904_write_reg(data, BANK_1,
reg2 + channel * 8, val);
else
ret = nct7904_write_reg(data, BANK_1,
reg3 + (channel - 5) * 4, val);
return ret;
}
static int nct7904_write_fan(struct device *dev, u32 attr, int channel,
long val)
{
struct nct7904_data *data = dev_get_drvdata(dev);
int ret;
u8 tmp;
switch (attr) {
case hwmon_fan_min:
if (val <= 0)
return -EINVAL;
val = clamp_val(DIV_ROUND_CLOSEST(1350000, val), 1, 0x1fff);
tmp = (val >> 5) & 0xff;
ret = nct7904_write_reg(data, BANK_1,
FANIN1_HV_HL_REG + channel * 2, tmp);
if (ret < 0)
return ret;
tmp = val & 0x1f;
ret = nct7904_write_reg(data, BANK_1,
FANIN1_LV_HL_REG + channel * 2, tmp);
return ret;
default:
return -EOPNOTSUPP;
}
}
static int nct7904_write_in(struct device *dev, u32 attr, int channel,
long val)
{
struct nct7904_data *data = dev_get_drvdata(dev);
int ret, index, tmp;
index = nct7904_chan_to_index[channel];
if (index < 14)
val = val / 2; /* 0.002V scale */
else
val = val / 6; /* 0.006V scale */
val = clamp_val(val, 0, 0x7ff);
switch (attr) {
case hwmon_in_min:
tmp = nct7904_read_reg(data, BANK_1,
VSEN1_LV_LL_REG + index * 4);
if (tmp < 0)
return tmp;
tmp &= ~0x7;
tmp |= val & 0x7;
ret = nct7904_write_reg(data, BANK_1,
VSEN1_LV_LL_REG + index * 4, tmp);
if (ret < 0)
return ret;
tmp = nct7904_read_reg(data, BANK_1,
VSEN1_HV_LL_REG + index * 4);
if (tmp < 0)
return tmp;
tmp = (val >> 3) & 0xff;
ret = nct7904_write_reg(data, BANK_1,
VSEN1_HV_LL_REG + index * 4, tmp);
return ret;
case hwmon_in_max:
tmp = nct7904_read_reg(data, BANK_1,
VSEN1_LV_HL_REG + index * 4);
if (tmp < 0)
return tmp;
tmp &= ~0x7;
tmp |= val & 0x7;
ret = nct7904_write_reg(data, BANK_1,
VSEN1_LV_HL_REG + index * 4, tmp);
if (ret < 0)
return ret;
tmp = nct7904_read_reg(data, BANK_1,
VSEN1_HV_HL_REG + index * 4);
if (tmp < 0)
return tmp;
tmp = (val >> 3) & 0xff;
ret = nct7904_write_reg(data, BANK_1,
VSEN1_HV_HL_REG + index * 4, tmp);
return ret;
default:
return -EOPNOTSUPP;
}
}
static int nct7904_write_pwm(struct device *dev, u32 attr, int channel,
long val)
{
......@@ -354,8 +691,14 @@ static int nct7904_write(struct device *dev, enum hwmon_sensor_types type,
u32 attr, int channel, long val)
{
switch (type) {
case hwmon_in:
return nct7904_write_in(dev, attr, channel, val);
case hwmon_fan:
return nct7904_write_fan(dev, attr, channel, val);
case hwmon_pwm:
return nct7904_write_pwm(dev, attr, channel, val);
case hwmon_temp:
return nct7904_write_temp(dev, attr, channel, val);
default:
return -EOPNOTSUPP;
}
......@@ -404,51 +747,91 @@ static int nct7904_detect(struct i2c_client *client,
static const struct hwmon_channel_info *nct7904_info[] = {
HWMON_CHANNEL_INFO(in,
HWMON_I_INPUT, /* dummy, skipped in is_visible */
HWMON_I_INPUT,
HWMON_I_INPUT,
HWMON_I_INPUT,
HWMON_I_INPUT,
HWMON_I_INPUT,
HWMON_I_INPUT,
HWMON_I_INPUT,
HWMON_I_INPUT,
HWMON_I_INPUT,
HWMON_I_INPUT,
HWMON_I_INPUT,
HWMON_I_INPUT,
HWMON_I_INPUT,
HWMON_I_INPUT,
HWMON_I_INPUT,
HWMON_I_INPUT,
HWMON_I_INPUT,
HWMON_I_INPUT,
HWMON_I_INPUT,
HWMON_I_INPUT),
/* dummy, skipped in is_visible */
HWMON_I_INPUT | HWMON_I_MIN | HWMON_I_MAX |
HWMON_I_ALARM,
HWMON_I_INPUT | HWMON_I_MIN | HWMON_I_MAX |
HWMON_I_ALARM,
HWMON_I_INPUT | HWMON_I_MIN | HWMON_I_MAX |
HWMON_I_ALARM,
HWMON_I_INPUT | HWMON_I_MIN | HWMON_I_MAX |
HWMON_I_ALARM,
HWMON_I_INPUT | HWMON_I_MIN | HWMON_I_MAX |
HWMON_I_ALARM,
HWMON_I_INPUT | HWMON_I_MIN | HWMON_I_MAX |
HWMON_I_ALARM,
HWMON_I_INPUT | HWMON_I_MIN | HWMON_I_MAX |
HWMON_I_ALARM,
HWMON_I_INPUT | HWMON_I_MIN | HWMON_I_MAX |
HWMON_I_ALARM,
HWMON_I_INPUT | HWMON_I_MIN | HWMON_I_MAX |
HWMON_I_ALARM,
HWMON_I_INPUT | HWMON_I_MIN | HWMON_I_MAX |
HWMON_I_ALARM,
HWMON_I_INPUT | HWMON_I_MIN | HWMON_I_MAX |
HWMON_I_ALARM,
HWMON_I_INPUT | HWMON_I_MIN | HWMON_I_MAX |
HWMON_I_ALARM,
HWMON_I_INPUT | HWMON_I_MIN | HWMON_I_MAX |
HWMON_I_ALARM,
HWMON_I_INPUT | HWMON_I_MIN | HWMON_I_MAX |
HWMON_I_ALARM,
HWMON_I_INPUT | HWMON_I_MIN | HWMON_I_MAX |
HWMON_I_ALARM,
HWMON_I_INPUT | HWMON_I_MIN | HWMON_I_MAX |
HWMON_I_ALARM,
HWMON_I_INPUT | HWMON_I_MIN | HWMON_I_MAX |
HWMON_I_ALARM,
HWMON_I_INPUT | HWMON_I_MIN | HWMON_I_MAX |
HWMON_I_ALARM,
HWMON_I_INPUT | HWMON_I_MIN | HWMON_I_MAX |
HWMON_I_ALARM,
HWMON_I_INPUT | HWMON_I_MIN | HWMON_I_MAX |
HWMON_I_ALARM,
HWMON_I_INPUT | HWMON_I_MIN | HWMON_I_MAX |
HWMON_I_ALARM),
HWMON_CHANNEL_INFO(fan,
HWMON_F_INPUT,
HWMON_F_INPUT,
HWMON_F_INPUT,
HWMON_F_INPUT,
HWMON_F_INPUT,
HWMON_F_INPUT,
HWMON_F_INPUT,
HWMON_F_INPUT),
HWMON_F_INPUT | HWMON_F_MIN | HWMON_F_ALARM,
HWMON_F_INPUT | HWMON_F_MIN | HWMON_F_ALARM,
HWMON_F_INPUT | HWMON_F_MIN | HWMON_F_ALARM,
HWMON_F_INPUT | HWMON_F_MIN | HWMON_F_ALARM,
HWMON_F_INPUT | HWMON_F_MIN | HWMON_F_ALARM,
HWMON_F_INPUT | HWMON_F_MIN | HWMON_F_ALARM,
HWMON_F_INPUT | HWMON_F_MIN | HWMON_F_ALARM,
HWMON_F_INPUT | HWMON_F_MIN | HWMON_F_ALARM),
HWMON_CHANNEL_INFO(pwm,
HWMON_PWM_INPUT | HWMON_PWM_ENABLE,
HWMON_PWM_INPUT | HWMON_PWM_ENABLE,
HWMON_PWM_INPUT | HWMON_PWM_ENABLE,
HWMON_PWM_INPUT | HWMON_PWM_ENABLE),
HWMON_CHANNEL_INFO(temp,
HWMON_T_INPUT,
HWMON_T_INPUT,
HWMON_T_INPUT,
HWMON_T_INPUT,
HWMON_T_INPUT,
HWMON_T_INPUT,
HWMON_T_INPUT,
HWMON_T_INPUT,
HWMON_T_INPUT),
HWMON_T_INPUT | HWMON_T_ALARM | HWMON_T_MAX |
HWMON_T_MAX_HYST | HWMON_T_TYPE | HWMON_T_CRIT |
HWMON_T_CRIT_HYST,
HWMON_T_INPUT | HWMON_T_ALARM | HWMON_T_MAX |
HWMON_T_MAX_HYST | HWMON_T_TYPE | HWMON_T_CRIT |
HWMON_T_CRIT_HYST,
HWMON_T_INPUT | HWMON_T_ALARM | HWMON_T_MAX |
HWMON_T_MAX_HYST | HWMON_T_TYPE | HWMON_T_CRIT |
HWMON_T_CRIT_HYST,
HWMON_T_INPUT | HWMON_T_ALARM | HWMON_T_MAX |
HWMON_T_MAX_HYST | HWMON_T_TYPE | HWMON_T_CRIT |
HWMON_T_CRIT_HYST,
HWMON_T_INPUT | HWMON_T_ALARM | HWMON_T_MAX |
HWMON_T_MAX_HYST | HWMON_T_TYPE | HWMON_T_CRIT |
HWMON_T_CRIT_HYST,
HWMON_T_INPUT | HWMON_T_ALARM | HWMON_T_MAX |
HWMON_T_MAX_HYST | HWMON_T_TYPE | HWMON_T_CRIT |
HWMON_T_CRIT_HYST,
HWMON_T_INPUT | HWMON_T_ALARM | HWMON_T_MAX |
HWMON_T_MAX_HYST | HWMON_T_TYPE | HWMON_T_CRIT |
HWMON_T_CRIT_HYST,
HWMON_T_INPUT | HWMON_T_ALARM | HWMON_T_MAX |
HWMON_T_MAX_HYST | HWMON_T_TYPE | HWMON_T_CRIT |
HWMON_T_CRIT_HYST,
HWMON_T_INPUT | HWMON_T_ALARM | HWMON_T_MAX |
HWMON_T_MAX_HYST | HWMON_T_TYPE | HWMON_T_CRIT |
HWMON_T_CRIT_HYST),
NULL
};
......@@ -530,11 +913,14 @@ static int nct7904_probe(struct i2c_client *client,
if (ret < 0)
return ret;
data->temp_mode = 0;
for (i = 0; i < 4; i++) {
val = (ret & (0x03 << i)) >> (i * 2);
bit = (1 << i);
if (val == 0)
data->tcpu_mask &= ~bit;
else if (val == 0x1 || val == 0x2)
data->temp_mode |= bit;
}
/* PECI */
......@@ -557,7 +943,7 @@ static int nct7904_probe(struct i2c_client *client,
if (ret < 0)
return ret;
data->has_dts = ret & 0xF;
if (data->enable_dts & 0x2) {
if (data->enable_dts & ENABLE_TSI) {
ret = nct7904_read_reg(data, BANK_0, DTS_T_CTRL1_REG);
if (ret < 0)
return ret;
......
......@@ -967,10 +967,8 @@ static int npcm7xx_pwm_fan_probe(struct platform_device *pdev)
spin_lock_init(&data->fan_lock[i]);
data->fan_irq[i] = platform_get_irq(pdev, i);
if (data->fan_irq[i] < 0) {
dev_err(dev, "get IRQ fan%d failed\n", i);
if (data->fan_irq[i] < 0)
return data->fan_irq[i];
}
sprintf(name, "NPCM7XX-FAN-MD%d", i);
ret = devm_request_irq(dev, data->fan_irq[i], npcm7xx_fan_isr,
......
......@@ -46,6 +46,15 @@ config SENSORS_IBM_CFFPS
This driver can also be built as a module. If so, the module will
be called ibm-cffps.
config SENSORS_INSPUR_IPSPS
tristate "INSPUR Power System Power Supply"
help
If you say yes here you get hardware monitoring support for the INSPUR
Power System power supply.
This driver can also be built as a module. If so, the module will
be called inspur-ipsps.
config SENSORS_IR35221
tristate "Infineon IR35221"
help
......
......@@ -7,6 +7,7 @@ obj-$(CONFIG_PMBUS) += pmbus_core.o
obj-$(CONFIG_SENSORS_PMBUS) += pmbus.o
obj-$(CONFIG_SENSORS_ADM1275) += adm1275.o
obj-$(CONFIG_SENSORS_IBM_CFFPS) += ibm-cffps.o
obj-$(CONFIG_SENSORS_INSPUR_IPSPS) += inspur-ipsps.o
obj-$(CONFIG_SENSORS_IR35221) += ir35221.o
obj-$(CONFIG_SENSORS_IR38064) += ir38064.o
obj-$(CONFIG_SENSORS_IRPS5401) += irps5401.o
......
......@@ -12,6 +12,7 @@
#include <linux/leds.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/of_device.h>
#include <linux/pmbus.h>
#include "pmbus.h"
......@@ -20,8 +21,9 @@
#define CFFPS_PN_CMD 0x9B
#define CFFPS_SN_CMD 0x9E
#define CFFPS_CCIN_CMD 0xBD
#define CFFPS_FW_CMD_START 0xFA
#define CFFPS_FW_NUM_BYTES 4
#define CFFPS_FW_CMD 0xFA
#define CFFPS1_FW_NUM_BYTES 4
#define CFFPS2_FW_NUM_WORDS 3
#define CFFPS_SYS_CONFIG_CMD 0xDA
#define CFFPS_INPUT_HISTORY_CMD 0xD6
......@@ -52,6 +54,8 @@ enum {
CFFPS_DEBUGFS_NUM_ENTRIES
};
enum versions { cffps1, cffps2 };
struct ibm_cffps_input_history {
struct mutex update_lock;
unsigned long last_update;
......@@ -61,6 +65,7 @@ struct ibm_cffps_input_history {
};
struct ibm_cffps {
enum versions version;
struct i2c_client *client;
struct ibm_cffps_input_history input_history;
......@@ -132,6 +137,8 @@ static ssize_t ibm_cffps_debugfs_op(struct file *file, char __user *buf,
struct ibm_cffps *psu = to_psu(idxp, idx);
char data[I2C_SMBUS_BLOCK_MAX] = { 0 };
pmbus_set_page(psu->client, 0);
switch (idx) {
case CFFPS_DEBUGFS_INPUT_HISTORY:
return ibm_cffps_read_input_history(psu, buf, count, ppos);
......@@ -152,16 +159,36 @@ static ssize_t ibm_cffps_debugfs_op(struct file *file, char __user *buf,
rc = snprintf(data, 5, "%04X", rc);
goto done;
case CFFPS_DEBUGFS_FW:
for (i = 0; i < CFFPS_FW_NUM_BYTES; ++i) {
rc = i2c_smbus_read_byte_data(psu->client,
CFFPS_FW_CMD_START + i);
if (rc < 0)
return rc;
switch (psu->version) {
case cffps1:
for (i = 0; i < CFFPS1_FW_NUM_BYTES; ++i) {
rc = i2c_smbus_read_byte_data(psu->client,
CFFPS_FW_CMD +
i);
if (rc < 0)
return rc;
snprintf(&data[i * 2], 3, "%02X", rc);
}
snprintf(&data[i * 2], 3, "%02X", rc);
}
rc = i * 2;
break;
case cffps2:
for (i = 0; i < CFFPS2_FW_NUM_WORDS; ++i) {
rc = i2c_smbus_read_word_data(psu->client,
CFFPS_FW_CMD +
i);
if (rc < 0)
return rc;
snprintf(&data[i * 4], 5, "%04X", rc);
}
rc = i * 2;
rc = i * 4;
break;
default:
return -EOPNOTSUPP;
}
goto done;
default:
return -EINVAL;
......@@ -279,6 +306,8 @@ static void ibm_cffps_led_brightness_set(struct led_classdev *led_cdev,
psu->led_state = CFFPS_LED_ON;
}
pmbus_set_page(psu->client, 0);
rc = i2c_smbus_write_byte_data(psu->client, CFFPS_SYS_CONFIG_CMD,
psu->led_state);
if (rc < 0)
......@@ -299,6 +328,8 @@ static int ibm_cffps_led_blink_set(struct led_classdev *led_cdev,
if (led_cdev->brightness == LED_OFF)
return 0;
pmbus_set_page(psu->client, 0);
rc = i2c_smbus_write_byte_data(psu->client, CFFPS_SYS_CONFIG_CMD,
CFFPS_LED_BLINK);
if (rc < 0)
......@@ -328,15 +359,32 @@ static void ibm_cffps_create_led_class(struct ibm_cffps *psu)
dev_warn(dev, "failed to register led class: %d\n", rc);
}
static struct pmbus_driver_info ibm_cffps_info = {
.pages = 1,
.func[0] = PMBUS_HAVE_VIN | PMBUS_HAVE_VOUT | PMBUS_HAVE_IOUT |
PMBUS_HAVE_PIN | PMBUS_HAVE_FAN12 | PMBUS_HAVE_TEMP |
PMBUS_HAVE_TEMP2 | PMBUS_HAVE_TEMP3 | PMBUS_HAVE_STATUS_VOUT |
PMBUS_HAVE_STATUS_IOUT | PMBUS_HAVE_STATUS_INPUT |
PMBUS_HAVE_STATUS_TEMP | PMBUS_HAVE_STATUS_FAN12,
.read_byte_data = ibm_cffps_read_byte_data,
.read_word_data = ibm_cffps_read_word_data,
static struct pmbus_driver_info ibm_cffps_info[] = {
[cffps1] = {
.pages = 1,
.func[0] = PMBUS_HAVE_VIN | PMBUS_HAVE_VOUT | PMBUS_HAVE_IOUT |
PMBUS_HAVE_PIN | PMBUS_HAVE_FAN12 | PMBUS_HAVE_TEMP |
PMBUS_HAVE_TEMP2 | PMBUS_HAVE_TEMP3 |
PMBUS_HAVE_STATUS_VOUT | PMBUS_HAVE_STATUS_IOUT |
PMBUS_HAVE_STATUS_INPUT | PMBUS_HAVE_STATUS_TEMP |
PMBUS_HAVE_STATUS_FAN12,
.read_byte_data = ibm_cffps_read_byte_data,
.read_word_data = ibm_cffps_read_word_data,
},
[cffps2] = {
.pages = 2,
.func[0] = PMBUS_HAVE_VIN | PMBUS_HAVE_VOUT | PMBUS_HAVE_IOUT |
PMBUS_HAVE_PIN | PMBUS_HAVE_FAN12 | PMBUS_HAVE_TEMP |
PMBUS_HAVE_TEMP2 | PMBUS_HAVE_TEMP3 |
PMBUS_HAVE_STATUS_VOUT | PMBUS_HAVE_STATUS_IOUT |
PMBUS_HAVE_STATUS_INPUT | PMBUS_HAVE_STATUS_TEMP |
PMBUS_HAVE_STATUS_FAN12,
.func[1] = PMBUS_HAVE_VOUT | PMBUS_HAVE_IOUT |
PMBUS_HAVE_TEMP | PMBUS_HAVE_TEMP2 | PMBUS_HAVE_TEMP3 |
PMBUS_HAVE_STATUS_VOUT | PMBUS_HAVE_STATUS_IOUT,
.read_byte_data = ibm_cffps_read_byte_data,
.read_word_data = ibm_cffps_read_word_data,
},
};
static struct pmbus_platform_data ibm_cffps_pdata = {
......@@ -347,12 +395,21 @@ static int ibm_cffps_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
int i, rc;
enum versions vs;
struct dentry *debugfs;
struct dentry *ibm_cffps_dir;
struct ibm_cffps *psu;
const void *md = of_device_get_match_data(&client->dev);
if (md)
vs = (enum versions)md;
else if (id)
vs = (enum versions)id->driver_data;
else
vs = cffps1;
client->dev.platform_data = &ibm_cffps_pdata;
rc = pmbus_do_probe(client, id, &ibm_cffps_info);
rc = pmbus_do_probe(client, id, &ibm_cffps_info[vs]);
if (rc)
return rc;
......@@ -364,6 +421,7 @@ static int ibm_cffps_probe(struct i2c_client *client,
if (!psu)
return 0;
psu->version = vs;
psu->client = client;
mutex_init(&psu->input_history.update_lock);
psu->input_history.last_update = jiffies - HZ;
......@@ -405,13 +463,21 @@ static int ibm_cffps_probe(struct i2c_client *client,
}
static const struct i2c_device_id ibm_cffps_id[] = {
{ "ibm_cffps1", 1 },
{ "ibm_cffps1", cffps1 },
{ "ibm_cffps2", cffps2 },
{}
};
MODULE_DEVICE_TABLE(i2c, ibm_cffps_id);
static const struct of_device_id ibm_cffps_of_match[] = {
{ .compatible = "ibm,cffps1" },
{
.compatible = "ibm,cffps1",
.data = (void *)cffps1
},
{
.compatible = "ibm,cffps2",
.data = (void *)cffps2
},
{}
};
MODULE_DEVICE_TABLE(of, ibm_cffps_of_match);
......
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright 2019 Inspur Corp.
*/
#include <linux/debugfs.h>
#include <linux/device.h>
#include <linux/fs.h>
#include <linux/i2c.h>
#include <linux/module.h>
#include <linux/pmbus.h>
#include <linux/hwmon-sysfs.h>
#include "pmbus.h"
#define IPSPS_REG_VENDOR_ID 0x99
#define IPSPS_REG_MODEL 0x9A
#define IPSPS_REG_FW_VERSION 0x9B
#define IPSPS_REG_PN 0x9C
#define IPSPS_REG_SN 0x9E
#define IPSPS_REG_HW_VERSION 0xB0
#define IPSPS_REG_MODE 0xFC
#define MODE_ACTIVE 0x55
#define MODE_STANDBY 0x0E
#define MODE_REDUNDANCY 0x00
#define MODE_ACTIVE_STRING "active"
#define MODE_STANDBY_STRING "standby"
#define MODE_REDUNDANCY_STRING "redundancy"
enum ipsps_index {
vendor,
model,
fw_version,
part_number,
serial_number,
hw_version,
mode,
num_regs,
};
static const u8 ipsps_regs[num_regs] = {
[vendor] = IPSPS_REG_VENDOR_ID,
[model] = IPSPS_REG_MODEL,
[fw_version] = IPSPS_REG_FW_VERSION,
[part_number] = IPSPS_REG_PN,
[serial_number] = IPSPS_REG_SN,
[hw_version] = IPSPS_REG_HW_VERSION,
[mode] = IPSPS_REG_MODE,
};
static ssize_t ipsps_string_show(struct device *dev,
struct device_attribute *devattr,
char *buf)
{
u8 reg;
int rc;
char *p;
char data[I2C_SMBUS_BLOCK_MAX + 1];
struct i2c_client *client = to_i2c_client(dev->parent);
struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
reg = ipsps_regs[attr->index];
rc = i2c_smbus_read_block_data(client, reg, data);
if (rc < 0)
return rc;
/* filled with printable characters, ending with # */
p = memscan(data, '#', rc);
*p = '\0';
return snprintf(buf, PAGE_SIZE, "%s\n", data);
}
static ssize_t ipsps_fw_version_show(struct device *dev,
struct device_attribute *devattr,
char *buf)
{
u8 reg;
int rc;
u8 data[I2C_SMBUS_BLOCK_MAX] = { 0 };
struct i2c_client *client = to_i2c_client(dev->parent);
struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
reg = ipsps_regs[attr->index];
rc = i2c_smbus_read_block_data(client, reg, data);
if (rc < 0)
return rc;
if (rc != 6)
return -EPROTO;
return snprintf(buf, PAGE_SIZE, "%u.%02u%u-%u.%02u\n",
data[1], data[2]/* < 100 */, data[3]/*< 10*/,
data[4], data[5]/* < 100 */);
}
static ssize_t ipsps_mode_show(struct device *dev,
struct device_attribute *devattr, char *buf)
{
u8 reg;
int rc;
struct i2c_client *client = to_i2c_client(dev->parent);
struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
reg = ipsps_regs[attr->index];
rc = i2c_smbus_read_byte_data(client, reg);
if (rc < 0)
return rc;
switch (rc) {
case MODE_ACTIVE:
return snprintf(buf, PAGE_SIZE, "[%s] %s %s\n",
MODE_ACTIVE_STRING,
MODE_STANDBY_STRING, MODE_REDUNDANCY_STRING);
case MODE_STANDBY:
return snprintf(buf, PAGE_SIZE, "%s [%s] %s\n",
MODE_ACTIVE_STRING,
MODE_STANDBY_STRING, MODE_REDUNDANCY_STRING);
case MODE_REDUNDANCY:
return snprintf(buf, PAGE_SIZE, "%s %s [%s]\n",
MODE_ACTIVE_STRING,
MODE_STANDBY_STRING, MODE_REDUNDANCY_STRING);
default:
return snprintf(buf, PAGE_SIZE, "unspecified\n");
}
}
static ssize_t ipsps_mode_store(struct device *dev,
struct device_attribute *devattr,
const char *buf, size_t count)
{
u8 reg;
int rc;
struct i2c_client *client = to_i2c_client(dev->parent);
struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
reg = ipsps_regs[attr->index];
if (sysfs_streq(MODE_STANDBY_STRING, buf)) {
rc = i2c_smbus_write_byte_data(client, reg,
MODE_STANDBY);
if (rc < 0)
return rc;
return count;
} else if (sysfs_streq(MODE_ACTIVE_STRING, buf)) {
rc = i2c_smbus_write_byte_data(client, reg,
MODE_ACTIVE);
if (rc < 0)
return rc;
return count;
}
return -EINVAL;
}
static SENSOR_DEVICE_ATTR_RO(vendor, ipsps_string, vendor);
static SENSOR_DEVICE_ATTR_RO(model, ipsps_string, model);
static SENSOR_DEVICE_ATTR_RO(part_number, ipsps_string, part_number);
static SENSOR_DEVICE_ATTR_RO(serial_number, ipsps_string, serial_number);
static SENSOR_DEVICE_ATTR_RO(hw_version, ipsps_string, hw_version);
static SENSOR_DEVICE_ATTR_RO(fw_version, ipsps_fw_version, fw_version);
static SENSOR_DEVICE_ATTR_RW(mode, ipsps_mode, mode);
static struct attribute *ipsps_attrs[] = {
&sensor_dev_attr_vendor.dev_attr.attr,
&sensor_dev_attr_model.dev_attr.attr,
&sensor_dev_attr_part_number.dev_attr.attr,
&sensor_dev_attr_serial_number.dev_attr.attr,
&sensor_dev_attr_hw_version.dev_attr.attr,
&sensor_dev_attr_fw_version.dev_attr.attr,
&sensor_dev_attr_mode.dev_attr.attr,
NULL,
};
ATTRIBUTE_GROUPS(ipsps);
static struct pmbus_driver_info ipsps_info = {
.pages = 1,
.func[0] = PMBUS_HAVE_VIN | PMBUS_HAVE_VOUT | PMBUS_HAVE_IOUT |
PMBUS_HAVE_IIN | PMBUS_HAVE_POUT | PMBUS_HAVE_PIN |
PMBUS_HAVE_FAN12 | PMBUS_HAVE_TEMP | PMBUS_HAVE_TEMP2 |
PMBUS_HAVE_TEMP3 | PMBUS_HAVE_STATUS_VOUT |
PMBUS_HAVE_STATUS_IOUT | PMBUS_HAVE_STATUS_INPUT |
PMBUS_HAVE_STATUS_TEMP | PMBUS_HAVE_STATUS_FAN12,
.groups = ipsps_groups,
};
static struct pmbus_platform_data ipsps_pdata = {
.flags = PMBUS_SKIP_STATUS_CHECK,
};
static int ipsps_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
client->dev.platform_data = &ipsps_pdata;
return pmbus_do_probe(client, id, &ipsps_info);
}
static const struct i2c_device_id ipsps_id[] = {
{ "ipsps1", 0 },
{}
};
MODULE_DEVICE_TABLE(i2c, ipsps_id);
#ifdef CONFIG_OF
static const struct of_device_id ipsps_of_match[] = {
{ .compatible = "inspur,ipsps1" },
{}
};
MODULE_DEVICE_TABLE(of, ipsps_of_match);
#endif
static struct i2c_driver ipsps_driver = {
.driver = {
.name = "inspur-ipsps",
.of_match_table = of_match_ptr(ipsps_of_match),
},
.probe = ipsps_probe,
.remove = pmbus_do_remove,
.id_table = ipsps_id,
};
module_i2c_driver(ipsps_driver);
MODULE_AUTHOR("John Wang");
MODULE_DESCRIPTION("PMBus driver for Inspur Power System power supplies");
MODULE_LICENSE("GPL");
......@@ -244,8 +244,6 @@ static int max31785_write_word_data(struct i2c_client *client, int page,
#define MAX31785_VOUT_FUNCS \
(PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT)
#define MAX37185_NUM_FAN_PAGES 6
static const struct pmbus_driver_info max31785_info = {
.pages = MAX31785_NR_PAGES,
......
......@@ -15,7 +15,6 @@
#include <linux/slab.h>
#include <linux/i2c.h>
#include <linux/pmbus.h>
#include <linux/gpio.h>
#include <linux/gpio/driver.h>
#include "pmbus.h"
......
......@@ -146,7 +146,7 @@ static struct platform_driver rpi_hwmon_driver = {
};
module_platform_driver(rpi_hwmon_driver);
MODULE_AUTHOR("Stefan Wahren <stefan.wahren@i2se.com>");
MODULE_AUTHOR("Stefan Wahren <wahrenst@gmx.net>");
MODULE_DESCRIPTION("Raspberry Pi voltage sensor driver");
MODULE_LICENSE("GPL v2");
MODULE_ALIAS("platform:raspberrypi-hwmon");
......@@ -24,19 +24,33 @@ static const unsigned char shtc1_cmd_measure_blocking_lpm[] = { 0x64, 0x58 };
static const unsigned char shtc1_cmd_measure_nonblocking_lpm[] = { 0x60, 0x9c };
/* command for reading the ID register */
static const unsigned char shtc1_cmd_read_id_reg[] = { 0xef, 0xc8 };
static const unsigned char shtc1_cmd_read_id_reg[] = { 0xef, 0xc8 };
/* constants for reading the ID register */
#define SHTC1_ID 0x07
#define SHTC1_ID_REG_MASK 0x1f
/*
* constants for reading the ID register
* SHTC1: 0x0007 with mask 0x003f
* SHTW1: 0x0007 with mask 0x003f
* SHTC3: 0x0807 with mask 0x083f
*/
#define SHTC3_ID 0x0807
#define SHTC3_ID_MASK 0x083f
#define SHTC1_ID 0x0007
#define SHTC1_ID_MASK 0x003f
/* delays for non-blocking i2c commands, both in us */
#define SHTC1_NONBLOCKING_WAIT_TIME_HPM 14400
#define SHTC1_NONBLOCKING_WAIT_TIME_LPM 1000
#define SHTC3_NONBLOCKING_WAIT_TIME_HPM 12100
#define SHTC3_NONBLOCKING_WAIT_TIME_LPM 800
#define SHTC1_CMD_LENGTH 2
#define SHTC1_RESPONSE_LENGTH 6
enum shtcx_chips {
shtc1,
shtc3,
};
struct shtc1_data {
struct i2c_client *client;
struct mutex update_lock;
......@@ -47,6 +61,7 @@ struct shtc1_data {
unsigned int nonblocking_wait_time; /* in us */
struct shtc1_platform_data setup;
enum shtcx_chips chip;
int temperature; /* 1000 * temperature in dgr C */
int humidity; /* 1000 * relative humidity in %RH */
......@@ -157,13 +172,16 @@ static void shtc1_select_command(struct shtc1_data *data)
data->command = data->setup.blocking_io ?
shtc1_cmd_measure_blocking_hpm :
shtc1_cmd_measure_nonblocking_hpm;
data->nonblocking_wait_time = SHTC1_NONBLOCKING_WAIT_TIME_HPM;
data->nonblocking_wait_time = (data->chip == shtc1) ?
SHTC1_NONBLOCKING_WAIT_TIME_HPM :
SHTC3_NONBLOCKING_WAIT_TIME_HPM;
} else {
data->command = data->setup.blocking_io ?
shtc1_cmd_measure_blocking_lpm :
shtc1_cmd_measure_nonblocking_lpm;
data->nonblocking_wait_time = SHTC1_NONBLOCKING_WAIT_TIME_LPM;
data->nonblocking_wait_time = (data->chip == shtc1) ?
SHTC1_NONBLOCKING_WAIT_TIME_LPM :
SHTC3_NONBLOCKING_WAIT_TIME_LPM;
}
}
......@@ -171,9 +189,11 @@ static int shtc1_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
int ret;
char id_reg[2];
u16 id_reg;
char id_reg_buf[2];
struct shtc1_data *data;
struct device *hwmon_dev;
enum shtcx_chips chip = id->driver_data;
struct i2c_adapter *adap = client->adapter;
struct device *dev = &client->dev;
......@@ -187,13 +207,20 @@ static int shtc1_probe(struct i2c_client *client,
dev_err(dev, "could not send read_id_reg command: %d\n", ret);
return ret < 0 ? ret : -ENODEV;
}
ret = i2c_master_recv(client, id_reg, sizeof(id_reg));
if (ret != sizeof(id_reg)) {
ret = i2c_master_recv(client, id_reg_buf, sizeof(id_reg_buf));
if (ret != sizeof(id_reg_buf)) {
dev_err(dev, "could not read ID register: %d\n", ret);
return -ENODEV;
}
if ((id_reg[1] & SHTC1_ID_REG_MASK) != SHTC1_ID) {
dev_err(dev, "ID register doesn't match\n");
id_reg = be16_to_cpup((__be16 *)id_reg_buf);
if (chip == shtc3) {
if ((id_reg & SHTC3_ID_MASK) != SHTC3_ID) {
dev_err(dev, "SHTC3 ID register does not match\n");
return -ENODEV;
}
} else if ((id_reg & SHTC1_ID_MASK) != SHTC1_ID) {
dev_err(dev, "SHTC1 ID register does not match\n");
return -ENODEV;
}
......@@ -204,6 +231,7 @@ static int shtc1_probe(struct i2c_client *client,
data->setup.blocking_io = false;
data->setup.high_precision = true;
data->client = client;
data->chip = chip;
if (client->dev.platform_data)
data->setup = *(struct shtc1_platform_data *)dev->platform_data;
......@@ -222,8 +250,9 @@ static int shtc1_probe(struct i2c_client *client,
/* device ID table */
static const struct i2c_device_id shtc1_id[] = {
{ "shtc1", 0 },
{ "shtw1", 0 },
{ "shtc1", shtc1 },
{ "shtw1", shtc1 },
{ "shtc3", shtc3 },
{ }
};
MODULE_DEVICE_TABLE(i2c, shtc1_id);
......
......@@ -586,10 +586,10 @@ static int smm665_probe(struct i2c_client *client,
data->client = client;
data->type = id->driver_data;
data->cmdreg = i2c_new_dummy(adapter, (client->addr & ~SMM665_REGMASK)
data->cmdreg = i2c_new_dummy_device(adapter, (client->addr & ~SMM665_REGMASK)
| SMM665_CMDREG_BASE);
if (!data->cmdreg)
return -ENOMEM;
if (IS_ERR(data->cmdreg))
return PTR_ERR(data->cmdreg);
switch (data->type) {
case smm465:
......
......@@ -894,12 +894,12 @@ w83781d_detect_subclients(struct i2c_client *new_client)
}
for (i = 0; i < num_sc; i++) {
data->lm75[i] = i2c_new_dummy(adapter, sc_addr[i]);
if (!data->lm75[i]) {
data->lm75[i] = i2c_new_dummy_device(adapter, sc_addr[i]);
if (IS_ERR(data->lm75[i])) {
dev_err(&new_client->dev,
"Subclient %d registration at address 0x%x failed.\n",
i, sc_addr[i]);
err = -ENOMEM;
err = PTR_ERR(data->lm75[i]);
if (i == 1)
goto ERROR_SC_3;
goto ERROR_SC_2;
......
......@@ -1260,7 +1260,7 @@ static int w83791d_detect_subclients(struct i2c_client *client)
struct i2c_adapter *adapter = client->adapter;
struct w83791d_data *data = i2c_get_clientdata(client);
int address = client->addr;
int i, id, err;
int i, id;
u8 val;
id = i2c_adapter_id(adapter);
......@@ -1272,8 +1272,7 @@ static int w83791d_detect_subclients(struct i2c_client *client)
"invalid subclient "
"address %d; must be 0x48-0x4f\n",
force_subclients[i]);
err = -ENODEV;
goto error_sc_0;
return -ENODEV;
}
}
w83791d_write(client, W83791D_REG_I2C_SUBADDR,
......@@ -1283,29 +1282,22 @@ static int w83791d_detect_subclients(struct i2c_client *client)
val = w83791d_read(client, W83791D_REG_I2C_SUBADDR);
if (!(val & 0x08))
data->lm75[0] = i2c_new_dummy(adapter, 0x48 + (val & 0x7));
data->lm75[0] = devm_i2c_new_dummy_device(&client->dev, adapter,
0x48 + (val & 0x7));
if (!(val & 0x80)) {
if ((data->lm75[0] != NULL) &&
if (!IS_ERR(data->lm75[0]) &&
((val & 0x7) == ((val >> 4) & 0x7))) {
dev_err(&client->dev,
"duplicate addresses 0x%x, "
"use force_subclient\n",
data->lm75[0]->addr);
err = -ENODEV;
goto error_sc_1;
return -ENODEV;
}
data->lm75[1] = i2c_new_dummy(adapter,
0x48 + ((val >> 4) & 0x7));
data->lm75[1] = devm_i2c_new_dummy_device(&client->dev, adapter,
0x48 + ((val >> 4) & 0x7));
}
return 0;
/* Undo inits in case of errors */
error_sc_1:
i2c_unregister_device(data->lm75[0]);
error_sc_0:
return err;
}
......@@ -1394,7 +1386,7 @@ static int w83791d_probe(struct i2c_client *client,
/* Register sysfs hooks */
err = sysfs_create_group(&client->dev.kobj, &w83791d_group);
if (err)
goto error3;
return err;
/* Check if pins of fan/pwm 4-5 are in use as GPIO */
has_fanpwm45 = w83791d_read(client, W83791D_REG_GPIO) & 0x10;
......@@ -1419,9 +1411,6 @@ static int w83791d_probe(struct i2c_client *client,
sysfs_remove_group(&client->dev.kobj, &w83791d_group_fanpwm45);
error4:
sysfs_remove_group(&client->dev.kobj, &w83791d_group);
error3:
i2c_unregister_device(data->lm75[0]);
i2c_unregister_device(data->lm75[1]);
return err;
}
......@@ -1432,9 +1421,6 @@ static int w83791d_remove(struct i2c_client *client)
hwmon_device_unregister(data->hwmon_dev);
sysfs_remove_group(&client->dev.kobj, &w83791d_group);
i2c_unregister_device(data->lm75[0]);
i2c_unregister_device(data->lm75[1]);
return 0;
}
......
......@@ -924,7 +924,7 @@ store_sf2_level(struct device *dev, struct device_attribute *attr,
static int
w83792d_detect_subclients(struct i2c_client *new_client)
{
int i, id, err;
int i, id;
int address = new_client->addr;
u8 val;
struct i2c_adapter *adapter = new_client->adapter;
......@@ -938,8 +938,7 @@ w83792d_detect_subclients(struct i2c_client *new_client)
dev_err(&new_client->dev,
"invalid subclient address %d; must be 0x48-0x4f\n",
force_subclients[i]);
err = -ENODEV;
goto ERROR_SC_0;
return -ENODEV;
}
}
w83792d_write_value(new_client, W83792D_REG_I2C_SUBADDR,
......@@ -949,28 +948,21 @@ w83792d_detect_subclients(struct i2c_client *new_client)
val = w83792d_read_value(new_client, W83792D_REG_I2C_SUBADDR);
if (!(val & 0x08))
data->lm75[0] = i2c_new_dummy(adapter, 0x48 + (val & 0x7));
data->lm75[0] = devm_i2c_new_dummy_device(&new_client->dev, adapter,
0x48 + (val & 0x7));
if (!(val & 0x80)) {
if ((data->lm75[0] != NULL) &&
if (!IS_ERR(data->lm75[0]) &&
((val & 0x7) == ((val >> 4) & 0x7))) {
dev_err(&new_client->dev,
"duplicate addresses 0x%x, use force_subclient\n",
data->lm75[0]->addr);
err = -ENODEV;
goto ERROR_SC_1;
return -ENODEV;
}
data->lm75[1] = i2c_new_dummy(adapter,
0x48 + ((val >> 4) & 0x7));
data->lm75[1] = devm_i2c_new_dummy_device(&new_client->dev, adapter,
0x48 + ((val >> 4) & 0x7));
}
return 0;
/* Undo inits in case of errors */
ERROR_SC_1:
i2c_unregister_device(data->lm75[0]);
ERROR_SC_0:
return err;
}
static SENSOR_DEVICE_ATTR(in0_input, S_IRUGO, show_in, NULL, 0);
......@@ -1396,7 +1388,7 @@ w83792d_probe(struct i2c_client *client, const struct i2c_device_id *id)
/* Register sysfs hooks */
err = sysfs_create_group(&dev->kobj, &w83792d_group);
if (err)
goto exit_i2c_unregister;
return err;
/*
* Read GPIO enable register to check if pins for fan 4,5 are used as
......@@ -1441,9 +1433,6 @@ w83792d_probe(struct i2c_client *client, const struct i2c_device_id *id)
sysfs_remove_group(&dev->kobj, &w83792d_group);
for (i = 0; i < ARRAY_SIZE(w83792d_group_fan); i++)
sysfs_remove_group(&dev->kobj, &w83792d_group_fan[i]);
exit_i2c_unregister:
i2c_unregister_device(data->lm75[0]);
i2c_unregister_device(data->lm75[1]);
return err;
}
......@@ -1459,9 +1448,6 @@ w83792d_remove(struct i2c_client *client)
sysfs_remove_group(&client->dev.kobj,
&w83792d_group_fan[i]);
i2c_unregister_device(data->lm75[0]);
i2c_unregister_device(data->lm75[1]);
return 0;
}
......
......@@ -1551,9 +1551,6 @@ static int w83793_remove(struct i2c_client *client)
for (i = 0; i < ARRAY_SIZE(w83793_temp); i++)
device_remove_file(dev, &w83793_temp[i].dev_attr);
i2c_unregister_device(data->lm75[0]);
i2c_unregister_device(data->lm75[1]);
/* Decrease data reference counter */
mutex_lock(&watchdog_data_mutex);
kref_put(&data->kref, w83793_release_resources);
......@@ -1565,7 +1562,7 @@ static int w83793_remove(struct i2c_client *client)
static int
w83793_detect_subclients(struct i2c_client *client)
{
int i, id, err;
int i, id;
int address = client->addr;
u8 tmp;
struct i2c_adapter *adapter = client->adapter;
......@@ -1580,8 +1577,7 @@ w83793_detect_subclients(struct i2c_client *client)
"invalid subclient "
"address %d; must be 0x48-0x4f\n",
force_subclients[i]);
err = -EINVAL;
goto ERROR_SC_0;
return -EINVAL;
}
}
w83793_write_value(client, W83793_REG_I2C_SUBADDR,
......@@ -1591,28 +1587,21 @@ w83793_detect_subclients(struct i2c_client *client)
tmp = w83793_read_value(client, W83793_REG_I2C_SUBADDR);
if (!(tmp & 0x08))
data->lm75[0] = i2c_new_dummy(adapter, 0x48 + (tmp & 0x7));
data->lm75[0] = devm_i2c_new_dummy_device(&client->dev, adapter,
0x48 + (tmp & 0x7));
if (!(tmp & 0x80)) {
if ((data->lm75[0] != NULL)
if (!IS_ERR(data->lm75[0])
&& ((tmp & 0x7) == ((tmp >> 4) & 0x7))) {
dev_err(&client->dev,
"duplicate addresses 0x%x, "
"use force_subclients\n", data->lm75[0]->addr);
err = -ENODEV;
goto ERROR_SC_1;
return -ENODEV;
}
data->lm75[1] = i2c_new_dummy(adapter,
0x48 + ((tmp >> 4) & 0x7));
data->lm75[1] = devm_i2c_new_dummy_device(&client->dev, adapter,
0x48 + ((tmp >> 4) & 0x7));
}
return 0;
/* Undo inits in case of errors */
ERROR_SC_1:
i2c_unregister_device(data->lm75[0]);
ERROR_SC_0:
return err;
}
/* Return 0 if detection is successful, -ENODEV otherwise */
......@@ -1945,9 +1934,6 @@ static int w83793_probe(struct i2c_client *client,
for (i = 0; i < ARRAY_SIZE(w83793_temp); i++)
device_remove_file(dev, &w83793_temp[i].dev_attr);
i2c_unregister_device(data->lm75[0]);
i2c_unregister_device(data->lm75[1]);
free_mem:
kfree(data);
exit:
......
......@@ -958,7 +958,7 @@ config TI_ADC161S626
config TI_ADS1015
tristate "Texas Instruments ADS1015 ADC"
depends on I2C && !SENSORS_ADS1015
depends on I2C
select REGMAP_I2C
select IIO_BUFFER
select IIO_TRIGGERED_BUFFER
......
......@@ -548,6 +548,7 @@
#define PCI_DEVICE_ID_AMD_17H_DF_F3 0x1463
#define PCI_DEVICE_ID_AMD_17H_M10H_DF_F3 0x15eb
#define PCI_DEVICE_ID_AMD_17H_M30H_DF_F3 0x1493
#define PCI_DEVICE_ID_AMD_17H_M70H_DF_F3 0x1443
#define PCI_DEVICE_ID_AMD_CNB17H_F3 0x1703
#define PCI_DEVICE_ID_AMD_LANCE 0x2000
#define PCI_DEVICE_ID_AMD_LANCE_HOME 0x2001
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment