Notable changes:
New driver for NCT7802Y Add support for TMP435, LM95233, LM95235, NCT6792D, and NXP LM75B Add regulator support for PMBus chips, specifically LTX2978 Add support for humidity sensors to iio-hwmon bridge driver -----BEGIN PGP SIGNATURE----- Version: GnuPG v1 iQIcBAABAgAGBQJUhgnFAAoJEMsfJm/On5mBbw4P/iswXZ6sekIMn/NKEXxXUgsd i9VpV+lsJu4TXYLozrwe6J50Wwk+AtRQurKMwq80pMglzz8VYtAyM6MLb84KFJtu U02cfmOP/KreB7Tt6YdVSRslWM07lq6tvJaCqQyuXDLyovL8i+C6ppStWbkqwaWS s4nTVnupxhudKQWBD2R14HcHP7Bk/QV0Nliuyi7jWs07a8g5I1f2UxFqUUiOPERl 8B0adCi+IJ3hWfrqSj+rCNkKStk4FoHt31t5Bs0ZsNcPcozLWtsCfaaxFcZy3Xal AkjpdCqIRkZ2DHA62VthR90VKKR8uL6hkJ9gfPq1Dz9keA9+NwZy60NLAUb8z4ck 2NXwJ7Mf4CBrU7IzqouZf6J1IPkfWxvdnReYstDpLaqFEgpRwJPU522Ci+EOiQCc WBjfo1BpiPyjGjel+TNVTBms0dxuX0jezJ8ZSIB9BfjirSXFTFUn4C4zK3yPNxAr jfWAWpTwUiLD01ZlFV0GBNax73oFAjKMDR06seGKxcEZNZbJW+FFpcAN3DjhcG+n U0WN3lh7Df8tqiuIQrhqAHVP2ApyLePjT/lg9zOUpL33jcM94sZ99HEPCwygkIhF wqC/eJx6jgOv+A0M38ux4zf+nhzUqqrpsdD63Msp4z9JOnMcMNZzBpBoc1aasQwL wrSMPt89hzVhfN0WIHvx =f0Ih -----END PGP SIGNATURE----- Merge tag 'hwmon-for-linus-3.19' of git://git.kernel.org/pub/scm/linux/kernel/git/groeck/linux-staging Pull hwmon updates from Guenter Roeck: "Notable changes: - new driver for NCT7802Y - support for TMP435, LM95233, LM95235, NCT6792D, and NXP LM75B - regulator support for PMBus chips, specifically LTX2978 - support for humidity sensors to iio-hwmon bridge driver * tag 'hwmon-for-linus-3.19' of git://git.kernel.org/pub/scm/linux/kernel/git/groeck/linux-staging: (21 commits) hwmon: (tmp401) Detect TMP435 on all addresses it supports hwmon: (lm75) Strengthen detect function hwmon: (gpio-fan) Add a shutdown handler to poweroff the fans hwmon: (gpio-fan) Allow usage of gpio operations that may sleep hwmon: (tmp401) Bail out from tmp401_probe() in case of write errors hwmon: (tmp401) Add support for TI TMP435 hwmon: (lm95234) Add support for LM95233 hwmon: (lm95245) Add support for LM95235 hwmon: (ina2xx) bail-out from ina2xx_probe() in case of configuration errors hwmon: (nct6775) Add blank lines after declarations hwmon: (nct6775) Add support for NCT6792D hwmon: (nct6775) Documentation updates hwmon: (lm75) Add support for the NXP LM75B hwmon: Driver for Nuvoton NCT7802Y hwmon: (ibmpowernv) Convert to module_platform_driver hwmon: (ibmpowernv) Use platform 'id_table' to probe the device hwmon: (iio_hwmon) Add support for humidity sensors hwmon: (ltc2978) Add regulator support hwmon: (pmbus) Add regulator support hwmon: (pmbus) add helpers for byte write and read modify write ...
This commit is contained in:
Коммит
177808cd28
|
@ -0,0 +1,39 @@
|
|||
ltc2978
|
||||
|
||||
Required properties:
|
||||
- compatible: should contain one of:
|
||||
* "lltc,ltc2974"
|
||||
* "lltc,ltc2977"
|
||||
* "lltc,ltc2978"
|
||||
* "lltc,ltc3880"
|
||||
* "lltc,ltc3883"
|
||||
* "lltc,ltm4676"
|
||||
- reg: I2C slave address
|
||||
|
||||
Optional properties:
|
||||
- regulators: A node that houses a sub-node for each regulator controlled by
|
||||
the device. Each sub-node is identified using the node's name, with valid
|
||||
values listed below. The content of each sub-node is defined by the
|
||||
standard binding for regulators; see regulator.txt.
|
||||
|
||||
Valid names of regulators depend on number of supplies supported per device:
|
||||
* ltc2974 : vout0 - vout3
|
||||
* ltc2977 : vout0 - vout7
|
||||
* ltc2978 : vout0 - vout7
|
||||
* ltc3880 : vout0 - vout1
|
||||
* ltc3883 : vout0
|
||||
* ltm4676 : vout0 - vout1
|
||||
|
||||
Example:
|
||||
ltc2978@5e {
|
||||
compatible = "lltc,ltc2978";
|
||||
reg = <0x5e>;
|
||||
regulators {
|
||||
vout0 {
|
||||
regulator-name = "FPGA-2.5V";
|
||||
};
|
||||
vout2 {
|
||||
regulator-name = "FPGA-1.5V";
|
||||
};
|
||||
};
|
||||
};
|
|
@ -53,6 +53,11 @@ Supported chips:
|
|||
http://www.ti.com/product/tmp75
|
||||
http://www.ti.com/product/tmp175
|
||||
http://www.ti.com/product/tmp275
|
||||
* NXP LM75B
|
||||
Prefix: 'lm75b'
|
||||
Addresses scanned: none
|
||||
Datasheet: Publicly available at the NXP website
|
||||
http://www.nxp.com/documents/data_sheet/LM75B.pdf
|
||||
|
||||
Author: Frodo Looijaard <frodol@dds.nl>
|
||||
|
||||
|
|
|
@ -2,6 +2,10 @@ Kernel driver lm95234
|
|||
=====================
|
||||
|
||||
Supported chips:
|
||||
* National Semiconductor / Texas Instruments LM95233
|
||||
Addresses scanned: I2C 0x18, 0x2a, 0x2b
|
||||
Datasheet: Publicly available at the Texas Instruments website
|
||||
http://www.ti.com/product/lm95233
|
||||
* National Semiconductor / Texas Instruments LM95234
|
||||
Addresses scanned: I2C 0x18, 0x4d, 0x4e
|
||||
Datasheet: Publicly available at the Texas Instruments website
|
||||
|
@ -13,11 +17,12 @@ Author: Guenter Roeck <linux@roeck-us.net>
|
|||
Description
|
||||
-----------
|
||||
|
||||
LM95234 is an 11-bit digital temperature sensor with a 2-wire System Management
|
||||
Bus (SMBus) interface and TrueTherm technology that can very accurately monitor
|
||||
the temperature of four remote diodes as well as its own temperature.
|
||||
The four remote diodes can be external devices such as microprocessors,
|
||||
graphics processors or diode-connected 2N3904s. The LM95234's TruTherm
|
||||
LM95233 and LM95234 are 11-bit digital temperature sensors with a 2-wire
|
||||
System Management Bus (SMBus) interface and TrueTherm technology
|
||||
that can very accurately monitor the temperature of two (LM95233)
|
||||
or four (LM95234) remote diodes as well as its own temperature.
|
||||
The remote diodes can be external devices such as microprocessors,
|
||||
graphics processors or diode-connected 2N3904s. The chip's TruTherm
|
||||
beta compensation technology allows sensing of 90 nm or 65 nm process
|
||||
thermal diodes accurately.
|
||||
|
||||
|
|
|
@ -2,10 +2,14 @@ Kernel driver lm95245
|
|||
==================
|
||||
|
||||
Supported chips:
|
||||
* National Semiconductor LM95245
|
||||
* TI LM95235
|
||||
Addresses scanned: I2C 0x18, 0x29, 0x4c
|
||||
Datasheet: Publicly available at the TI website
|
||||
http://www.ti.com/lit/ds/symlink/lm95235.pdf
|
||||
* TI / National Semiconductor LM95245
|
||||
Addresses scanned: I2C 0x18, 0x19, 0x29, 0x4c, 0x4d
|
||||
Datasheet: Publicly available at the National Semiconductor website
|
||||
http://www.national.com/mpf/LM/LM95245.html
|
||||
Datasheet: Publicly available at the TI website
|
||||
http://www.ti.com/lit/ds/symlink/lm95245.pdf
|
||||
|
||||
|
||||
Author: Alexander Stein <alexander.stein@systec-electronic.com>
|
||||
|
@ -13,10 +17,10 @@ Author: Alexander Stein <alexander.stein@systec-electronic.com>
|
|||
Description
|
||||
-----------
|
||||
|
||||
The LM95245 is an 11-bit digital temperature sensor with a 2-wire System
|
||||
LM95235 and LM95245 are 11-bit digital temperature sensors with a 2-wire System
|
||||
Management Bus (SMBus) interface and TruTherm technology that can monitor
|
||||
the temperature of a remote diode as well as its own temperature.
|
||||
The LM95245 can be used to very accurately monitor the temperature of
|
||||
The chips can be used to very accurately monitor the temperature of
|
||||
external devices such as microprocessors.
|
||||
|
||||
All temperature values are given in millidegrees Celsius. Local temperature
|
||||
|
|
|
@ -8,11 +8,15 @@ Kernel driver NCT6775
|
|||
=====================
|
||||
|
||||
Supported chips:
|
||||
* Nuvoton NCT6102D/NCT6104D/NCT6106D
|
||||
Prefix: 'nct6106'
|
||||
Addresses scanned: ISA address retrieved from Super I/O registers
|
||||
Datasheet: Available from the Nuvoton web site
|
||||
* Nuvoton NCT5572D/NCT6771F/NCT6772F/NCT6775F/W83677HG-I
|
||||
Prefix: 'nct6775'
|
||||
Addresses scanned: ISA address retrieved from Super I/O registers
|
||||
Datasheet: Available from Nuvoton upon request
|
||||
* Nuvoton NCT5577D/NCT6776D/NCT6776F
|
||||
* Nuvoton NCT5573D/NCT5577D/NCT6776D/NCT6776F
|
||||
Prefix: 'nct6776'
|
||||
Addresses scanned: ISA address retrieved from Super I/O registers
|
||||
Datasheet: Available from Nuvoton upon request
|
||||
|
@ -20,6 +24,14 @@ Supported chips:
|
|||
Prefix: 'nct6779'
|
||||
Addresses scanned: ISA address retrieved from Super I/O registers
|
||||
Datasheet: Available from Nuvoton upon request
|
||||
* Nuvoton NCT6791D
|
||||
Prefix: 'nct6791'
|
||||
Addresses scanned: ISA address retrieved from Super I/O registers
|
||||
Datasheet: Available from Nuvoton upon request
|
||||
* Nuvoton NCT6792D
|
||||
Prefix: 'nct6792'
|
||||
Addresses scanned: ISA address retrieved from Super I/O registers
|
||||
Datasheet: Available from Nuvoton upon request
|
||||
|
||||
Authors:
|
||||
Guenter Roeck <linux@roeck-us.net>
|
||||
|
|
|
@ -0,0 +1,32 @@
|
|||
Kernel driver nct7802
|
||||
=====================
|
||||
|
||||
Supported chips:
|
||||
* Nuvoton NCT7802Y
|
||||
Prefix: 'nct7802'
|
||||
Addresses scanned: I2C 0x28..0x2f
|
||||
Datasheet: Available from Nuvoton web site
|
||||
|
||||
Authors:
|
||||
Guenter Roeck <linux@roeck-us.net>
|
||||
|
||||
Description
|
||||
-----------
|
||||
|
||||
This driver implements support for the Nuvoton NCT7802Y hardware monitoring
|
||||
chip. NCT7802Y supports 6 temperature sensors, 5 voltage sensors, and 3 fan
|
||||
speed sensors.
|
||||
|
||||
The chip also supports intelligent fan speed control. This functionality is
|
||||
not currently supported by the driver.
|
||||
|
||||
Tested Boards and BIOS Versions
|
||||
-------------------------------
|
||||
|
||||
The driver has been reported to work with the following boards and
|
||||
BIOS versions.
|
||||
|
||||
Board BIOS version
|
||||
---------------------------------------------------------------
|
||||
Kontron COMe-bSC2 CHR2E934.001.GGO
|
||||
Kontron COMe-bIP2 CCR2E212
|
|
@ -18,6 +18,10 @@ Supported chips:
|
|||
Prefix: 'tmp432'
|
||||
Addresses scanned: I2C 0x4c, 0x4d
|
||||
Datasheet: http://focus.ti.com/docs/prod/folders/print/tmp432.html
|
||||
* Texas Instruments TMP435
|
||||
Prefix: 'tmp435'
|
||||
Addresses scanned: I2C 0x37, 0x48 - 0x4f
|
||||
Datasheet: http://focus.ti.com/docs/prod/folders/print/tmp435.html
|
||||
|
||||
Authors:
|
||||
Hans de Goede <hdegoede@redhat.com>
|
||||
|
@ -27,8 +31,8 @@ Description
|
|||
-----------
|
||||
|
||||
This driver implements support for Texas Instruments TMP401, TMP411,
|
||||
TMP431, and TMP432 chips. These chips implement one or two remote and
|
||||
one local temperature sensors. Temperature is measured in degrees
|
||||
TMP431, TMP432 and TMP435 chips. These chips implement one or two remote
|
||||
and one local temperature sensors. Temperature is measured in degrees
|
||||
Celsius. Resolution of the remote sensor is 0.0625 degree. Local
|
||||
sensor resolution can be set to 0.5, 0.25, 0.125 or 0.0625 degree (not
|
||||
supported by the driver so far, so using the default resolution of 0.5
|
||||
|
|
|
@ -20,7 +20,9 @@
|
|||
|
||||
#include <linux/delay.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/of_platform.h>
|
||||
#include <asm/opal.h>
|
||||
#include <asm/machdep.h>
|
||||
|
||||
static DEFINE_MUTEX(opal_sensor_mutex);
|
||||
|
||||
|
@ -64,3 +66,21 @@ out:
|
|||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(opal_get_sensor_data);
|
||||
|
||||
static __init int opal_sensor_init(void)
|
||||
{
|
||||
struct platform_device *pdev;
|
||||
struct device_node *sensor;
|
||||
|
||||
sensor = of_find_node_by_path("/ibm,opal/sensors");
|
||||
if (!sensor) {
|
||||
pr_err("Opal node 'sensors' not found\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
pdev = of_platform_device_create(sensor, "opal-sensor", NULL);
|
||||
of_node_put(sensor);
|
||||
|
||||
return PTR_ERR_OR_ZERO(pdev);
|
||||
}
|
||||
machine_subsys_initcall(powernv, opal_sensor_init);
|
||||
|
|
|
@ -1028,11 +1028,11 @@ config SENSORS_LM93
|
|||
will be called lm93.
|
||||
|
||||
config SENSORS_LM95234
|
||||
tristate "National Semiconductor LM95234"
|
||||
tristate "National Semiconductor LM95234 and compatibles"
|
||||
depends on I2C
|
||||
help
|
||||
If you say yes here you get support for the LM95234 temperature
|
||||
sensor.
|
||||
If you say yes here you get support for the LM95233 and LM95234
|
||||
temperature sensor chips.
|
||||
|
||||
This driver can also be built as a module. If so, the module
|
||||
will be called lm95234.
|
||||
|
@ -1048,10 +1048,11 @@ config SENSORS_LM95241
|
|||
will be called lm95241.
|
||||
|
||||
config SENSORS_LM95245
|
||||
tristate "National Semiconductor LM95245 sensor chip"
|
||||
tristate "National Semiconductor LM95245 and compatibles"
|
||||
depends on I2C
|
||||
help
|
||||
If you say yes here you get support for LM95245 sensor chip.
|
||||
If you say yes here you get support for LM95235 and LM95245
|
||||
temperature sensor chips.
|
||||
|
||||
This driver can also be built as a module. If so, the module
|
||||
will be called lm95245.
|
||||
|
@ -1117,12 +1118,23 @@ config SENSORS_NCT6775
|
|||
help
|
||||
If you say yes here you get support for the hardware monitoring
|
||||
functionality of the Nuvoton NCT6106D, NCT6775F, NCT6776F, NCT6779D,
|
||||
NCT6791D and compatible Super-I/O chips. This driver replaces the
|
||||
w83627ehf driver for NCT6775F and NCT6776F.
|
||||
NCT6791D, NCT6792D and compatible Super-I/O chips. This driver
|
||||
replaces the w83627ehf driver for NCT6775F and NCT6776F.
|
||||
|
||||
This driver can also be built as a module. If so, the module
|
||||
will be called nct6775.
|
||||
|
||||
config SENSORS_NCT7802
|
||||
tristate "Nuvoton NCT7802Y"
|
||||
depends on I2C
|
||||
select REGMAP_I2C
|
||||
help
|
||||
If you say yes here you get support for the Nuvoton NCT7802Y
|
||||
hardware monitoring chip.
|
||||
|
||||
This driver can also be built as a module. If so, the module
|
||||
will be called nct7802.
|
||||
|
||||
config SENSORS_PCF8591
|
||||
tristate "Philips PCF8591 ADC/DAC"
|
||||
depends on I2C
|
||||
|
@ -1454,7 +1466,7 @@ config SENSORS_TMP401
|
|||
depends on I2C
|
||||
help
|
||||
If you say yes here you get support for Texas Instruments TMP401,
|
||||
TMP411, TMP431, and TMP432 temperature sensor chips.
|
||||
TMP411, TMP431, TMP432 and TMP435 temperature sensor chips.
|
||||
|
||||
This driver can also be built as a module. If so, the module
|
||||
will be called tmp401.
|
||||
|
|
|
@ -118,6 +118,7 @@ obj-$(CONFIG_SENSORS_MCP3021) += mcp3021.o
|
|||
obj-$(CONFIG_SENSORS_MENF21BMC_HWMON) += menf21bmc_hwmon.o
|
||||
obj-$(CONFIG_SENSORS_NCT6683) += nct6683.o
|
||||
obj-$(CONFIG_SENSORS_NCT6775) += nct6775.o
|
||||
obj-$(CONFIG_SENSORS_NCT7802) += nct7802.o
|
||||
obj-$(CONFIG_SENSORS_NTC_THERMISTOR) += ntc_thermistor.o
|
||||
obj-$(CONFIG_SENSORS_PC87360) += pc87360.o
|
||||
obj-$(CONFIG_SENSORS_PC87427) += pc87427.o
|
||||
|
|
|
@ -79,7 +79,7 @@ static ssize_t show_fan_alarm(struct device *dev,
|
|||
{
|
||||
struct gpio_fan_data *fan_data = dev_get_drvdata(dev);
|
||||
struct gpio_fan_alarm *alarm = fan_data->alarm;
|
||||
int value = gpio_get_value(alarm->gpio);
|
||||
int value = gpio_get_value_cansleep(alarm->gpio);
|
||||
|
||||
if (alarm->active_low)
|
||||
value = !value;
|
||||
|
@ -131,7 +131,7 @@ static void __set_fan_ctrl(struct gpio_fan_data *fan_data, int ctrl_val)
|
|||
int i;
|
||||
|
||||
for (i = 0; i < fan_data->num_ctrl; i++)
|
||||
gpio_set_value(fan_data->ctrl[i], (ctrl_val >> i) & 1);
|
||||
gpio_set_value_cansleep(fan_data->ctrl[i], (ctrl_val >> i) & 1);
|
||||
}
|
||||
|
||||
static int __get_fan_ctrl(struct gpio_fan_data *fan_data)
|
||||
|
@ -142,7 +142,7 @@ static int __get_fan_ctrl(struct gpio_fan_data *fan_data)
|
|||
for (i = 0; i < fan_data->num_ctrl; i++) {
|
||||
int value;
|
||||
|
||||
value = gpio_get_value(fan_data->ctrl[i]);
|
||||
value = gpio_get_value_cansleep(fan_data->ctrl[i]);
|
||||
ctrl_val |= (value << i);
|
||||
}
|
||||
return ctrl_val;
|
||||
|
@ -369,7 +369,8 @@ static int fan_ctrl_init(struct gpio_fan_data *fan_data,
|
|||
if (err)
|
||||
return err;
|
||||
|
||||
err = gpio_direction_output(ctrl[i], gpio_get_value(ctrl[i]));
|
||||
err = gpio_direction_output(ctrl[i],
|
||||
gpio_get_value_cansleep(ctrl[i]));
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
|
@ -549,6 +550,14 @@ static int gpio_fan_probe(struct platform_device *pdev)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static void gpio_fan_shutdown(struct platform_device *pdev)
|
||||
{
|
||||
struct gpio_fan_data *fan_data = dev_get_drvdata(&pdev->dev);
|
||||
|
||||
if (fan_data->ctrl)
|
||||
set_fan_speed(fan_data, 0);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
static int gpio_fan_suspend(struct device *dev)
|
||||
{
|
||||
|
@ -580,6 +589,7 @@ static SIMPLE_DEV_PM_OPS(gpio_fan_pm, gpio_fan_suspend, gpio_fan_resume);
|
|||
|
||||
static struct platform_driver gpio_fan_driver = {
|
||||
.probe = gpio_fan_probe,
|
||||
.shutdown = gpio_fan_shutdown,
|
||||
.driver = {
|
||||
.name = "gpio-fan",
|
||||
.pm = GPIO_FAN_PM,
|
||||
|
|
|
@ -74,9 +74,6 @@ struct platform_data {
|
|||
u32 sensors_count; /* Total count of sensors from each group */
|
||||
};
|
||||
|
||||
/* Platform device representing all the ibmpowernv sensors */
|
||||
static struct platform_device *pdevice;
|
||||
|
||||
static ssize_t show_sensor(struct device *dev, struct device_attribute *devattr,
|
||||
char *buf)
|
||||
{
|
||||
|
@ -99,7 +96,7 @@ static ssize_t show_sensor(struct device *dev, struct device_attribute *devattr,
|
|||
return sprintf(buf, "%u\n", x);
|
||||
}
|
||||
|
||||
static int __init get_sensor_index_attr(const char *name, u32 *index,
|
||||
static int get_sensor_index_attr(const char *name, u32 *index,
|
||||
char *attr)
|
||||
{
|
||||
char *hash_pos = strchr(name, '#');
|
||||
|
@ -136,7 +133,7 @@ static int __init get_sensor_index_attr(const char *name, u32 *index,
|
|||
* which need to be mapped as fan2_input, temp1_max respectively before
|
||||
* populating them inside hwmon device class.
|
||||
*/
|
||||
static int __init create_hwmon_attr_name(struct device *dev, enum sensors type,
|
||||
static int create_hwmon_attr_name(struct device *dev, enum sensors type,
|
||||
const char *node_name,
|
||||
char *hwmon_attr_name)
|
||||
{
|
||||
|
@ -172,7 +169,7 @@ static int __init create_hwmon_attr_name(struct device *dev, enum sensors type,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int __init populate_attr_groups(struct platform_device *pdev)
|
||||
static int populate_attr_groups(struct platform_device *pdev)
|
||||
{
|
||||
struct platform_data *pdata = platform_get_drvdata(pdev);
|
||||
const struct attribute_group **pgroups = pdata->attr_groups;
|
||||
|
@ -180,11 +177,6 @@ static int __init populate_attr_groups(struct platform_device *pdev)
|
|||
enum sensors type;
|
||||
|
||||
opal = of_find_node_by_path("/ibm,opal/sensors");
|
||||
if (!opal) {
|
||||
dev_dbg(&pdev->dev, "Opal node 'sensors' not found\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
for_each_child_of_node(opal, np) {
|
||||
if (np->name == NULL)
|
||||
continue;
|
||||
|
@ -221,7 +213,7 @@ static int __init populate_attr_groups(struct platform_device *pdev)
|
|||
* to the name required by the higher 'hwmon' driver like fan1_input, temp1_max
|
||||
* etc..
|
||||
*/
|
||||
static int __init create_device_attrs(struct platform_device *pdev)
|
||||
static int create_device_attrs(struct platform_device *pdev)
|
||||
{
|
||||
struct platform_data *pdata = platform_get_drvdata(pdev);
|
||||
const struct attribute_group **pgroups = pdata->attr_groups;
|
||||
|
@ -280,7 +272,7 @@ exit_put_node:
|
|||
return err;
|
||||
}
|
||||
|
||||
static int __init ibmpowernv_probe(struct platform_device *pdev)
|
||||
static int ibmpowernv_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct platform_data *pdata;
|
||||
struct device *hwmon_dev;
|
||||
|
@ -309,57 +301,25 @@ static int __init ibmpowernv_probe(struct platform_device *pdev)
|
|||
return PTR_ERR_OR_ZERO(hwmon_dev);
|
||||
}
|
||||
|
||||
static const struct platform_device_id opal_sensor_driver_ids[] = {
|
||||
{
|
||||
.name = "opal-sensor",
|
||||
},
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(platform, opal_sensor_driver_ids);
|
||||
|
||||
static struct platform_driver ibmpowernv_driver = {
|
||||
.driver = {
|
||||
.owner = THIS_MODULE,
|
||||
.name = DRVNAME,
|
||||
.probe = ibmpowernv_probe,
|
||||
.id_table = opal_sensor_driver_ids,
|
||||
.driver = {
|
||||
.owner = THIS_MODULE,
|
||||
.name = DRVNAME,
|
||||
},
|
||||
};
|
||||
|
||||
static int __init ibmpowernv_init(void)
|
||||
{
|
||||
int err;
|
||||
|
||||
pdevice = platform_device_alloc(DRVNAME, 0);
|
||||
if (!pdevice) {
|
||||
pr_err("Device allocation failed\n");
|
||||
err = -ENOMEM;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
err = platform_device_add(pdevice);
|
||||
if (err) {
|
||||
pr_err("Device addition failed (%d)\n", err);
|
||||
goto exit_device_put;
|
||||
}
|
||||
|
||||
err = platform_driver_probe(&ibmpowernv_driver, ibmpowernv_probe);
|
||||
if (err) {
|
||||
if (err != -ENODEV)
|
||||
pr_err("Platform driver probe failed (%d)\n", err);
|
||||
|
||||
goto exit_device_del;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
exit_device_del:
|
||||
platform_device_del(pdevice);
|
||||
exit_device_put:
|
||||
platform_device_put(pdevice);
|
||||
exit:
|
||||
return err;
|
||||
}
|
||||
|
||||
static void __exit ibmpowernv_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&ibmpowernv_driver);
|
||||
platform_device_unregister(pdevice);
|
||||
}
|
||||
module_platform_driver(ibmpowernv_driver);
|
||||
|
||||
MODULE_AUTHOR("Neelesh Gupta <neelegup@linux.vnet.ibm.com>");
|
||||
MODULE_DESCRIPTION("IBM POWERNV platform sensors");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
module_init(ibmpowernv_init);
|
||||
module_exit(ibmpowernv_exit);
|
||||
|
|
|
@ -63,7 +63,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;
|
||||
int in_i = 1, temp_i = 1, curr_i = 1, humidity_i = 1;
|
||||
enum iio_chan_type type;
|
||||
struct iio_channel *channels;
|
||||
const char *name = "iio_hwmon";
|
||||
|
@ -123,6 +123,11 @@ static int iio_hwmon_probe(struct platform_device *pdev)
|
|||
"curr%d_input",
|
||||
curr_i++);
|
||||
break;
|
||||
case IIO_HUMIDITYRELATIVE:
|
||||
a->dev_attr.attr.name = kasprintf(GFP_KERNEL,
|
||||
"humidity%d_input",
|
||||
humidity_i++);
|
||||
break;
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
goto error_release_channels;
|
||||
|
|
|
@ -223,6 +223,7 @@ static int ina2xx_probe(struct i2c_client *client,
|
|||
struct device *hwmon_dev;
|
||||
long shunt = 10000; /* default shunt value 10mOhms */
|
||||
u32 val;
|
||||
int ret;
|
||||
|
||||
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_WORD_DATA))
|
||||
return -ENODEV;
|
||||
|
@ -247,12 +248,25 @@ static int ina2xx_probe(struct i2c_client *client,
|
|||
data->config = &ina2xx_config[data->kind];
|
||||
|
||||
/* device configuration */
|
||||
i2c_smbus_write_word_swapped(client, INA2XX_CONFIG,
|
||||
data->config->config_default);
|
||||
/* set current LSB to 1mA, shunt is in uOhms */
|
||||
/* (equation 13 in datasheet) */
|
||||
i2c_smbus_write_word_swapped(client, INA2XX_CALIBRATION,
|
||||
data->config->calibration_factor / shunt);
|
||||
ret = i2c_smbus_write_word_swapped(client, INA2XX_CONFIG,
|
||||
data->config->config_default);
|
||||
if (ret < 0) {
|
||||
dev_err(dev,
|
||||
"error writing to the config register: %d", ret);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
/*
|
||||
* Set current LSB to 1mA, shunt is in uOhms
|
||||
* (equation 13 in datasheet).
|
||||
*/
|
||||
ret = i2c_smbus_write_word_swapped(client, INA2XX_CALIBRATION,
|
||||
data->config->calibration_factor / shunt);
|
||||
if (ret < 0) {
|
||||
dev_err(dev,
|
||||
"error writing to the calibration register: %d", ret);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
data->client = client;
|
||||
mutex_init(&data->update_lock);
|
||||
|
|
|
@ -44,6 +44,7 @@ enum lm75_type { /* keep sorted in alphabetical order */
|
|||
g751,
|
||||
lm75,
|
||||
lm75a,
|
||||
lm75b,
|
||||
max6625,
|
||||
max6626,
|
||||
mcp980x,
|
||||
|
@ -233,6 +234,10 @@ lm75_probe(struct i2c_client *client, const struct i2c_device_id *id)
|
|||
data->resolution = 9;
|
||||
data->sample_time = HZ / 2;
|
||||
break;
|
||||
case lm75b:
|
||||
data->resolution = 11;
|
||||
data->sample_time = HZ / 4;
|
||||
break;
|
||||
case max6625:
|
||||
data->resolution = 9;
|
||||
data->sample_time = HZ / 4;
|
||||
|
@ -322,6 +327,7 @@ static const struct i2c_device_id lm75_ids[] = {
|
|||
{ "g751", g751, },
|
||||
{ "lm75", lm75, },
|
||||
{ "lm75a", lm75a, },
|
||||
{ "lm75b", lm75b, },
|
||||
{ "max6625", max6625, },
|
||||
{ "max6626", max6626, },
|
||||
{ "mcp980x", mcp980x, },
|
||||
|
@ -409,6 +415,12 @@ static int lm75_detect(struct i2c_client *new_client,
|
|||
|| i2c_smbus_read_byte_data(new_client, 7) != os)
|
||||
return -ENODEV;
|
||||
}
|
||||
/*
|
||||
* It is very unlikely that this is a LM75 if both
|
||||
* hysteresis and temperature limit registers are 0.
|
||||
*/
|
||||
if (hyst == 0 && os == 0)
|
||||
return -ENODEV;
|
||||
|
||||
/* Addresses cycling */
|
||||
for (i = 8; i <= 248; i += 40) {
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
/*
|
||||
* Driver for Texas Instruments / National Semiconductor LM95234
|
||||
*
|
||||
* Copyright (c) 2013 Guenter Roeck <linux@roeck-us.net>
|
||||
* Copyright (c) 2013, 2014 Guenter Roeck <linux@roeck-us.net>
|
||||
*
|
||||
* Derived from lm95241.c
|
||||
* Copyright (C) 2008, 2010 Davide Rizzo <elpa.rizzo@gmail.com>
|
||||
|
@ -30,7 +30,10 @@
|
|||
|
||||
#define DRVNAME "lm95234"
|
||||
|
||||
static const unsigned short normal_i2c[] = { 0x18, 0x4d, 0x4e, I2C_CLIENT_END };
|
||||
enum chips { lm95233, lm95234 };
|
||||
|
||||
static const unsigned short normal_i2c[] = {
|
||||
0x18, 0x2a, 0x2b, 0x4d, 0x4e, I2C_CLIENT_END };
|
||||
|
||||
/* LM95234 registers */
|
||||
#define LM95234_REG_MAN_ID 0xFE
|
||||
|
@ -53,11 +56,13 @@ static const unsigned short normal_i2c[] = { 0x18, 0x4d, 0x4e, I2C_CLIENT_END };
|
|||
#define LM95234_REG_TCRIT_HYST 0x5a
|
||||
|
||||
#define NATSEMI_MAN_ID 0x01
|
||||
#define LM95233_CHIP_ID 0x89
|
||||
#define LM95234_CHIP_ID 0x79
|
||||
|
||||
/* Client data (each client gets its own) */
|
||||
struct lm95234_data {
|
||||
struct i2c_client *client;
|
||||
const struct attribute_group *groups[3];
|
||||
struct mutex update_lock;
|
||||
unsigned long last_updated, interval; /* in jiffies */
|
||||
bool valid; /* false until following fields are valid */
|
||||
|
@ -564,35 +569,23 @@ static SENSOR_DEVICE_ATTR(temp5_offset, S_IWUSR | S_IRUGO, show_offset,
|
|||
static DEVICE_ATTR(update_interval, S_IWUSR | S_IRUGO, show_interval,
|
||||
set_interval);
|
||||
|
||||
static struct attribute *lm95234_attrs[] = {
|
||||
static struct attribute *lm95234_common_attrs[] = {
|
||||
&sensor_dev_attr_temp1_input.dev_attr.attr,
|
||||
&sensor_dev_attr_temp2_input.dev_attr.attr,
|
||||
&sensor_dev_attr_temp3_input.dev_attr.attr,
|
||||
&sensor_dev_attr_temp4_input.dev_attr.attr,
|
||||
&sensor_dev_attr_temp5_input.dev_attr.attr,
|
||||
&sensor_dev_attr_temp2_fault.dev_attr.attr,
|
||||
&sensor_dev_attr_temp3_fault.dev_attr.attr,
|
||||
&sensor_dev_attr_temp4_fault.dev_attr.attr,
|
||||
&sensor_dev_attr_temp5_fault.dev_attr.attr,
|
||||
&sensor_dev_attr_temp2_type.dev_attr.attr,
|
||||
&sensor_dev_attr_temp3_type.dev_attr.attr,
|
||||
&sensor_dev_attr_temp4_type.dev_attr.attr,
|
||||
&sensor_dev_attr_temp5_type.dev_attr.attr,
|
||||
&sensor_dev_attr_temp1_max.dev_attr.attr,
|
||||
&sensor_dev_attr_temp2_max.dev_attr.attr,
|
||||
&sensor_dev_attr_temp3_max.dev_attr.attr,
|
||||
&sensor_dev_attr_temp4_max.dev_attr.attr,
|
||||
&sensor_dev_attr_temp5_max.dev_attr.attr,
|
||||
&sensor_dev_attr_temp1_max_hyst.dev_attr.attr,
|
||||
&sensor_dev_attr_temp2_max_hyst.dev_attr.attr,
|
||||
&sensor_dev_attr_temp3_max_hyst.dev_attr.attr,
|
||||
&sensor_dev_attr_temp4_max_hyst.dev_attr.attr,
|
||||
&sensor_dev_attr_temp5_max_hyst.dev_attr.attr,
|
||||
&sensor_dev_attr_temp1_max_alarm.dev_attr.attr,
|
||||
&sensor_dev_attr_temp2_max_alarm.dev_attr.attr,
|
||||
&sensor_dev_attr_temp3_max_alarm.dev_attr.attr,
|
||||
&sensor_dev_attr_temp4_max_alarm.dev_attr.attr,
|
||||
&sensor_dev_attr_temp5_max_alarm.dev_attr.attr,
|
||||
&sensor_dev_attr_temp2_crit.dev_attr.attr,
|
||||
&sensor_dev_attr_temp3_crit.dev_attr.attr,
|
||||
&sensor_dev_attr_temp2_crit_hyst.dev_attr.attr,
|
||||
|
@ -601,18 +594,44 @@ static struct attribute *lm95234_attrs[] = {
|
|||
&sensor_dev_attr_temp3_crit_alarm.dev_attr.attr,
|
||||
&sensor_dev_attr_temp2_offset.dev_attr.attr,
|
||||
&sensor_dev_attr_temp3_offset.dev_attr.attr,
|
||||
&sensor_dev_attr_temp4_offset.dev_attr.attr,
|
||||
&sensor_dev_attr_temp5_offset.dev_attr.attr,
|
||||
&dev_attr_update_interval.attr,
|
||||
NULL
|
||||
};
|
||||
ATTRIBUTE_GROUPS(lm95234);
|
||||
|
||||
static const struct attribute_group lm95234_common_group = {
|
||||
.attrs = lm95234_common_attrs,
|
||||
};
|
||||
|
||||
static struct attribute *lm95234_attrs[] = {
|
||||
&sensor_dev_attr_temp4_input.dev_attr.attr,
|
||||
&sensor_dev_attr_temp5_input.dev_attr.attr,
|
||||
&sensor_dev_attr_temp4_fault.dev_attr.attr,
|
||||
&sensor_dev_attr_temp5_fault.dev_attr.attr,
|
||||
&sensor_dev_attr_temp4_type.dev_attr.attr,
|
||||
&sensor_dev_attr_temp5_type.dev_attr.attr,
|
||||
&sensor_dev_attr_temp4_max.dev_attr.attr,
|
||||
&sensor_dev_attr_temp5_max.dev_attr.attr,
|
||||
&sensor_dev_attr_temp4_max_hyst.dev_attr.attr,
|
||||
&sensor_dev_attr_temp5_max_hyst.dev_attr.attr,
|
||||
&sensor_dev_attr_temp4_max_alarm.dev_attr.attr,
|
||||
&sensor_dev_attr_temp5_max_alarm.dev_attr.attr,
|
||||
&sensor_dev_attr_temp4_offset.dev_attr.attr,
|
||||
&sensor_dev_attr_temp5_offset.dev_attr.attr,
|
||||
NULL
|
||||
};
|
||||
|
||||
static const struct attribute_group lm95234_group = {
|
||||
.attrs = lm95234_attrs,
|
||||
};
|
||||
|
||||
static int lm95234_detect(struct i2c_client *client,
|
||||
struct i2c_board_info *info)
|
||||
{
|
||||
struct i2c_adapter *adapter = client->adapter;
|
||||
int address = client->addr;
|
||||
u8 config_mask, model_mask;
|
||||
int mfg_id, chip_id, val;
|
||||
const char *name;
|
||||
|
||||
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
|
||||
return -ENODEV;
|
||||
|
@ -622,15 +641,31 @@ static int lm95234_detect(struct i2c_client *client,
|
|||
return -ENODEV;
|
||||
|
||||
chip_id = i2c_smbus_read_byte_data(client, LM95234_REG_CHIP_ID);
|
||||
if (chip_id != LM95234_CHIP_ID)
|
||||
switch (chip_id) {
|
||||
case LM95233_CHIP_ID:
|
||||
if (address != 0x18 && address != 0x2a && address != 0x2b)
|
||||
return -ENODEV;
|
||||
config_mask = 0xbf;
|
||||
model_mask = 0xf9;
|
||||
name = "lm95233";
|
||||
break;
|
||||
case LM95234_CHIP_ID:
|
||||
if (address != 0x18 && address != 0x4d && address != 0x4e)
|
||||
return -ENODEV;
|
||||
config_mask = 0xbc;
|
||||
model_mask = 0xe1;
|
||||
name = "lm95234";
|
||||
break;
|
||||
default:
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
val = i2c_smbus_read_byte_data(client, LM95234_REG_STATUS);
|
||||
if (val & 0x30)
|
||||
return -ENODEV;
|
||||
|
||||
val = i2c_smbus_read_byte_data(client, LM95234_REG_CONFIG);
|
||||
if (val & 0xbc)
|
||||
if (val & config_mask)
|
||||
return -ENODEV;
|
||||
|
||||
val = i2c_smbus_read_byte_data(client, LM95234_REG_CONVRATE);
|
||||
|
@ -638,14 +673,14 @@ static int lm95234_detect(struct i2c_client *client,
|
|||
return -ENODEV;
|
||||
|
||||
val = i2c_smbus_read_byte_data(client, LM95234_REG_REM_MODEL);
|
||||
if (val & 0xe1)
|
||||
if (val & model_mask)
|
||||
return -ENODEV;
|
||||
|
||||
val = i2c_smbus_read_byte_data(client, LM95234_REG_REM_MODEL_STS);
|
||||
if (val & 0xe1)
|
||||
if (val & model_mask)
|
||||
return -ENODEV;
|
||||
|
||||
strlcpy(info->type, "lm95234", I2C_NAME_SIZE);
|
||||
strlcpy(info->type, name, I2C_NAME_SIZE);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -698,15 +733,19 @@ static int lm95234_probe(struct i2c_client *client,
|
|||
if (err < 0)
|
||||
return err;
|
||||
|
||||
data->groups[0] = &lm95234_common_group;
|
||||
if (id->driver_data == lm95234)
|
||||
data->groups[1] = &lm95234_group;
|
||||
|
||||
hwmon_dev = devm_hwmon_device_register_with_groups(dev, client->name,
|
||||
data,
|
||||
lm95234_groups);
|
||||
data, data->groups);
|
||||
return PTR_ERR_OR_ZERO(hwmon_dev);
|
||||
}
|
||||
|
||||
/* Driver data (common to all clients) */
|
||||
static const struct i2c_device_id lm95234_id[] = {
|
||||
{ "lm95234", 0 },
|
||||
{ "lm95233", lm95233 },
|
||||
{ "lm95234", lm95234 },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, lm95234_id);
|
||||
|
@ -725,5 +764,5 @@ static struct i2c_driver lm95234_driver = {
|
|||
module_i2c_driver(lm95234_driver);
|
||||
|
||||
MODULE_AUTHOR("Guenter Roeck <linux@roeck-us.net>");
|
||||
MODULE_DESCRIPTION("LM95234 sensor driver");
|
||||
MODULE_DESCRIPTION("LM95233/LM95234 sensor driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
|
|
@ -1,10 +1,8 @@
|
|||
/*
|
||||
* Copyright (C) 2011 Alexander Stein <alexander.stein@systec-electronic.com>
|
||||
*
|
||||
* The LM95245 is a sensor chip made by National Semiconductors.
|
||||
* The LM95245 is a sensor chip made by TI / National Semiconductor.
|
||||
* It reports up to two temperatures (its own plus an external one).
|
||||
* Complete datasheet can be obtained from National's website at:
|
||||
* http://www.national.com/ds.cgi/LM/LM95245.pdf
|
||||
*
|
||||
* This driver is based on lm95241.c
|
||||
*
|
||||
|
@ -34,8 +32,6 @@
|
|||
#include <linux/mutex.h>
|
||||
#include <linux/sysfs.h>
|
||||
|
||||
#define DEVNAME "lm95245"
|
||||
|
||||
static const unsigned short normal_i2c[] = {
|
||||
0x18, 0x19, 0x29, 0x4c, 0x4d, I2C_CLIENT_END };
|
||||
|
||||
|
@ -98,7 +94,8 @@ static const unsigned short normal_i2c[] = {
|
|||
#define STATUS1_LOC 0x01
|
||||
|
||||
#define MANUFACTURER_ID 0x01
|
||||
#define DEFAULT_REVISION 0xB3
|
||||
#define LM95235_REVISION 0xB1
|
||||
#define LM95245_REVISION 0xB3
|
||||
|
||||
static const u8 lm95245_reg_address[] = {
|
||||
LM95245_REG_R_LOCAL_TEMPH_S,
|
||||
|
@ -427,17 +424,32 @@ static int lm95245_detect(struct i2c_client *new_client,
|
|||
struct i2c_board_info *info)
|
||||
{
|
||||
struct i2c_adapter *adapter = new_client->adapter;
|
||||
int address = new_client->addr;
|
||||
const char *name;
|
||||
int rev, id;
|
||||
|
||||
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
|
||||
return -ENODEV;
|
||||
|
||||
if (i2c_smbus_read_byte_data(new_client, LM95245_REG_R_MAN_ID)
|
||||
!= MANUFACTURER_ID
|
||||
|| i2c_smbus_read_byte_data(new_client, LM95245_REG_R_CHIP_ID)
|
||||
!= DEFAULT_REVISION)
|
||||
id = i2c_smbus_read_byte_data(new_client, LM95245_REG_R_MAN_ID);
|
||||
if (id != MANUFACTURER_ID)
|
||||
return -ENODEV;
|
||||
|
||||
strlcpy(info->type, DEVNAME, I2C_NAME_SIZE);
|
||||
rev = i2c_smbus_read_byte_data(new_client, LM95245_REG_R_CHIP_ID);
|
||||
switch (rev) {
|
||||
case LM95235_REVISION:
|
||||
if (address != 0x18 && address != 0x29 && address != 0x4c)
|
||||
return -ENODEV;
|
||||
name = "lm95235";
|
||||
break;
|
||||
case LM95245_REVISION:
|
||||
name = "lm95245";
|
||||
break;
|
||||
default:
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
strlcpy(info->type, name, I2C_NAME_SIZE);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -484,7 +496,8 @@ static int lm95245_probe(struct i2c_client *client,
|
|||
|
||||
/* Driver data (common to all clients) */
|
||||
static const struct i2c_device_id lm95245_id[] = {
|
||||
{ DEVNAME, 0 },
|
||||
{ "lm95235", 0 },
|
||||
{ "lm95245", 0 },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, lm95245_id);
|
||||
|
@ -492,7 +505,7 @@ MODULE_DEVICE_TABLE(i2c, lm95245_id);
|
|||
static struct i2c_driver lm95245_driver = {
|
||||
.class = I2C_CLASS_HWMON,
|
||||
.driver = {
|
||||
.name = DEVNAME,
|
||||
.name = "lm95245",
|
||||
},
|
||||
.probe = lm95245_probe,
|
||||
.id_table = lm95245_id,
|
||||
|
@ -503,5 +516,5 @@ static struct i2c_driver lm95245_driver = {
|
|||
module_i2c_driver(lm95245_driver);
|
||||
|
||||
MODULE_AUTHOR("Alexander Stein <alexander.stein@systec-electronic.com>");
|
||||
MODULE_DESCRIPTION("LM95245 sensor driver");
|
||||
MODULE_DESCRIPTION("LM95235/LM95245 sensor driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
|
|
@ -38,6 +38,7 @@
|
|||
* nct6776f 9 5 3 6+3 0xc330 0xc1 0x5ca3
|
||||
* nct6779d 15 5 5 2+6 0xc560 0xc1 0x5ca3
|
||||
* nct6791d 15 6 6 2+6 0xc800 0xc1 0x5ca3
|
||||
* nct6792d 15 6 6 2+6 0xc910 0xc1 0x5ca3
|
||||
*
|
||||
* #temp lists the number of monitored temperature sources (first value) plus
|
||||
* the number of directly connectable temperature sensors (second value).
|
||||
|
@ -61,7 +62,7 @@
|
|||
|
||||
#define USE_ALTERNATE
|
||||
|
||||
enum kinds { nct6106, nct6775, nct6776, nct6779, nct6791 };
|
||||
enum kinds { nct6106, nct6775, nct6776, nct6779, nct6791, nct6792 };
|
||||
|
||||
/* used to set data->name = nct6775_device_names[data->sio_kind] */
|
||||
static const char * const nct6775_device_names[] = {
|
||||
|
@ -70,6 +71,7 @@ static const char * const nct6775_device_names[] = {
|
|||
"nct6776",
|
||||
"nct6779",
|
||||
"nct6791",
|
||||
"nct6792",
|
||||
};
|
||||
|
||||
static unsigned short force_id;
|
||||
|
@ -100,6 +102,7 @@ MODULE_PARM_DESC(fan_debounce, "Enable debouncing for fan RPM signal");
|
|||
#define SIO_NCT6776_ID 0xc330
|
||||
#define SIO_NCT6779_ID 0xc560
|
||||
#define SIO_NCT6791_ID 0xc800
|
||||
#define SIO_NCT6792_ID 0xc910
|
||||
#define SIO_ID_MASK 0xFFF0
|
||||
|
||||
enum pwm_enable { off, manual, thermal_cruise, speed_cruise, sf3, sf4 };
|
||||
|
@ -529,6 +532,12 @@ static const s8 NCT6791_ALARM_BITS[] = {
|
|||
4, 5, 13, -1, -1, -1, /* temp1..temp6 */
|
||||
12, 9 }; /* intrusion0, intrusion1 */
|
||||
|
||||
/* NCT6792 specific data */
|
||||
|
||||
static const u16 NCT6792_REG_TEMP_MON[] = {
|
||||
0x73, 0x75, 0x77, 0x79, 0x7b, 0x7d };
|
||||
static const u16 NCT6792_REG_BEEP[NUM_REG_BEEP] = {
|
||||
0xb2, 0xb3, 0xb4, 0xb5, 0xbf };
|
||||
|
||||
/* NCT6102D/NCT6106D specific data */
|
||||
|
||||
|
@ -1043,13 +1052,14 @@ static bool is_word_sized(struct nct6775_data *data, u16 reg)
|
|||
reg == 0x73 || reg == 0x75 || reg == 0x77;
|
||||
case nct6779:
|
||||
case nct6791:
|
||||
case nct6792:
|
||||
return reg == 0x150 || reg == 0x153 || reg == 0x155 ||
|
||||
((reg & 0xfff0) == 0x4b0 && (reg & 0x000f) < 0x0b) ||
|
||||
reg == 0x402 ||
|
||||
reg == 0x63a || reg == 0x63c || reg == 0x63e ||
|
||||
reg == 0x640 || reg == 0x642 ||
|
||||
reg == 0x73 || reg == 0x75 || reg == 0x77 || reg == 0x79 ||
|
||||
reg == 0x7b;
|
||||
reg == 0x7b || reg == 0x7d;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
@ -1063,6 +1073,7 @@ static bool is_word_sized(struct nct6775_data *data, u16 reg)
|
|||
static inline void nct6775_set_bank(struct nct6775_data *data, u16 reg)
|
||||
{
|
||||
u8 bank = reg >> 8;
|
||||
|
||||
if (data->bank != bank) {
|
||||
outb_p(NCT6775_REG_BANK, data->addr + ADDR_REG_OFFSET);
|
||||
outb_p(bank, data->addr + DATA_REG_OFFSET);
|
||||
|
@ -1300,6 +1311,7 @@ static void nct6775_update_pwm(struct device *dev)
|
|||
if (!data->target_speed_tolerance[i] ||
|
||||
data->pwm_enable[i] == speed_cruise) {
|
||||
u8 t = fanmodecfg & 0x0f;
|
||||
|
||||
if (data->REG_TOLERANCE_H) {
|
||||
t |= (nct6775_read_value(data,
|
||||
data->REG_TOLERANCE_H[i]) & 0x70) >> 1;
|
||||
|
@ -1391,6 +1403,7 @@ static void nct6775_update_pwm_limits(struct device *dev)
|
|||
case nct6106:
|
||||
case nct6779:
|
||||
case nct6791:
|
||||
case nct6792:
|
||||
reg = nct6775_read_value(data,
|
||||
data->REG_CRITICAL_PWM_ENABLE[i]);
|
||||
if (reg & data->CRITICAL_PWM_ENABLE_MASK)
|
||||
|
@ -1473,6 +1486,7 @@ static struct nct6775_data *nct6775_update_device(struct device *dev)
|
|||
data->alarms = 0;
|
||||
for (i = 0; i < NUM_REG_ALARM; i++) {
|
||||
u8 alarm;
|
||||
|
||||
if (!data->REG_ALARM[i])
|
||||
continue;
|
||||
alarm = nct6775_read_value(data, data->REG_ALARM[i]);
|
||||
|
@ -1482,6 +1496,7 @@ static struct nct6775_data *nct6775_update_device(struct device *dev)
|
|||
data->beeps = 0;
|
||||
for (i = 0; i < NUM_REG_BEEP; i++) {
|
||||
u8 beep;
|
||||
|
||||
if (!data->REG_BEEP[i])
|
||||
continue;
|
||||
beep = nct6775_read_value(data, data->REG_BEEP[i]);
|
||||
|
@ -1504,8 +1519,9 @@ show_in_reg(struct device *dev, struct device_attribute *attr, char *buf)
|
|||
{
|
||||
struct nct6775_data *data = nct6775_update_device(dev);
|
||||
struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr);
|
||||
int nr = sattr->nr;
|
||||
int index = sattr->index;
|
||||
int nr = sattr->nr;
|
||||
|
||||
return sprintf(buf, "%ld\n", in_from_reg(data->in[nr][index], nr));
|
||||
}
|
||||
|
||||
|
@ -1515,10 +1531,12 @@ store_in_reg(struct device *dev, struct device_attribute *attr, const char *buf,
|
|||
{
|
||||
struct nct6775_data *data = dev_get_drvdata(dev);
|
||||
struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr);
|
||||
int nr = sattr->nr;
|
||||
int index = sattr->index;
|
||||
int nr = sattr->nr;
|
||||
unsigned long val;
|
||||
int err = kstrtoul(buf, 10, &val);
|
||||
int err;
|
||||
|
||||
err = kstrtoul(buf, 10, &val);
|
||||
if (err < 0)
|
||||
return err;
|
||||
mutex_lock(&data->update_lock);
|
||||
|
@ -1535,6 +1553,7 @@ show_alarm(struct device *dev, struct device_attribute *attr, char *buf)
|
|||
struct nct6775_data *data = nct6775_update_device(dev);
|
||||
struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr);
|
||||
int nr = data->ALARM_BITS[sattr->index];
|
||||
|
||||
return sprintf(buf, "%u\n",
|
||||
(unsigned int)((data->alarms >> nr) & 0x01));
|
||||
}
|
||||
|
@ -1570,6 +1589,7 @@ show_temp_alarm(struct device *dev, struct device_attribute *attr, char *buf)
|
|||
nr = find_temp_source(data, sattr->index, data->num_temp_alarms);
|
||||
if (nr >= 0) {
|
||||
int bit = data->ALARM_BITS[nr + TEMP_ALARM_BASE];
|
||||
|
||||
alarm = (data->alarms >> bit) & 0x01;
|
||||
}
|
||||
return sprintf(buf, "%u\n", alarm);
|
||||
|
@ -1595,8 +1615,9 @@ store_beep(struct device *dev, struct device_attribute *attr, const char *buf,
|
|||
int nr = data->BEEP_BITS[sattr->index];
|
||||
int regindex = nr >> 3;
|
||||
unsigned long val;
|
||||
int err;
|
||||
|
||||
int err = kstrtoul(buf, 10, &val);
|
||||
err = kstrtoul(buf, 10, &val);
|
||||
if (err < 0)
|
||||
return err;
|
||||
if (val > 1)
|
||||
|
@ -1629,6 +1650,7 @@ show_temp_beep(struct device *dev, struct device_attribute *attr, char *buf)
|
|||
nr = find_temp_source(data, sattr->index, data->num_temp_beeps);
|
||||
if (nr >= 0) {
|
||||
int bit = data->BEEP_BITS[nr + TEMP_ALARM_BASE];
|
||||
|
||||
beep = (data->beeps >> bit) & 0x01;
|
||||
}
|
||||
return sprintf(buf, "%u\n", beep);
|
||||
|
@ -1642,8 +1664,9 @@ store_temp_beep(struct device *dev, struct device_attribute *attr,
|
|||
struct nct6775_data *data = dev_get_drvdata(dev);
|
||||
int nr, bit, regindex;
|
||||
unsigned long val;
|
||||
int err;
|
||||
|
||||
int err = kstrtoul(buf, 10, &val);
|
||||
err = kstrtoul(buf, 10, &val);
|
||||
if (err < 0)
|
||||
return err;
|
||||
if (val > 1)
|
||||
|
@ -1715,6 +1738,7 @@ show_fan(struct device *dev, struct device_attribute *attr, char *buf)
|
|||
struct nct6775_data *data = nct6775_update_device(dev);
|
||||
struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr);
|
||||
int nr = sattr->index;
|
||||
|
||||
return sprintf(buf, "%d\n", data->rpm[nr]);
|
||||
}
|
||||
|
||||
|
@ -1724,6 +1748,7 @@ show_fan_min(struct device *dev, struct device_attribute *attr, char *buf)
|
|||
struct nct6775_data *data = nct6775_update_device(dev);
|
||||
struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr);
|
||||
int nr = sattr->index;
|
||||
|
||||
return sprintf(buf, "%d\n",
|
||||
data->fan_from_reg_min(data->fan_min[nr],
|
||||
data->fan_div[nr]));
|
||||
|
@ -1735,6 +1760,7 @@ show_fan_div(struct device *dev, struct device_attribute *attr, char *buf)
|
|||
struct nct6775_data *data = nct6775_update_device(dev);
|
||||
struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr);
|
||||
int nr = sattr->index;
|
||||
|
||||
return sprintf(buf, "%u\n", div_from_reg(data->fan_div[nr]));
|
||||
}
|
||||
|
||||
|
@ -1746,9 +1772,9 @@ store_fan_min(struct device *dev, struct device_attribute *attr,
|
|||
struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr);
|
||||
int nr = sattr->index;
|
||||
unsigned long val;
|
||||
int err;
|
||||
unsigned int reg;
|
||||
u8 new_div;
|
||||
int err;
|
||||
|
||||
err = kstrtoul(buf, 10, &val);
|
||||
if (err < 0)
|
||||
|
@ -1932,6 +1958,7 @@ show_temp_label(struct device *dev, struct device_attribute *attr, char *buf)
|
|||
struct nct6775_data *data = nct6775_update_device(dev);
|
||||
struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr);
|
||||
int nr = sattr->index;
|
||||
|
||||
return sprintf(buf, "%s\n", data->temp_label[data->temp_src[nr]]);
|
||||
}
|
||||
|
||||
|
@ -2008,6 +2035,7 @@ show_temp_type(struct device *dev, struct device_attribute *attr, char *buf)
|
|||
struct nct6775_data *data = nct6775_update_device(dev);
|
||||
struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr);
|
||||
int nr = sattr->index;
|
||||
|
||||
return sprintf(buf, "%d\n", (int)data->temp_type[nr]);
|
||||
}
|
||||
|
||||
|
@ -2790,6 +2818,7 @@ store_auto_pwm(struct device *dev, struct device_attribute *attr,
|
|||
case nct6106:
|
||||
case nct6779:
|
||||
case nct6791:
|
||||
case nct6792:
|
||||
nct6775_write_value(data, data->REG_CRITICAL_PWM[nr],
|
||||
val);
|
||||
reg = nct6775_read_value(data,
|
||||
|
@ -2997,6 +3026,7 @@ static ssize_t
|
|||
show_vid(struct device *dev, struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct nct6775_data *data = dev_get_drvdata(dev);
|
||||
|
||||
return sprintf(buf, "%d\n", vid_from_reg(data->vid, data->vrm));
|
||||
}
|
||||
|
||||
|
@ -3202,7 +3232,7 @@ nct6775_check_fan_inputs(struct nct6775_data *data)
|
|||
pwm4pin = false;
|
||||
pwm5pin = false;
|
||||
pwm6pin = false;
|
||||
} else { /* NCT6779D or NCT6791D */
|
||||
} else { /* NCT6779D, NCT6791D, or NCT6792D */
|
||||
regval = superio_inb(sioreg, 0x1c);
|
||||
|
||||
fan3pin = !(regval & (1 << 5));
|
||||
|
@ -3215,7 +3245,7 @@ nct6775_check_fan_inputs(struct nct6775_data *data)
|
|||
|
||||
fan4min = fan4pin;
|
||||
|
||||
if (data->kind == nct6791) {
|
||||
if (data->kind == nct6791 || data->kind == nct6792) {
|
||||
regval = superio_inb(sioreg, 0x2d);
|
||||
fan6pin = (regval & (1 << 1));
|
||||
pwm6pin = (regval & (1 << 0));
|
||||
|
@ -3588,6 +3618,7 @@ static int nct6775_probe(struct platform_device *pdev)
|
|||
|
||||
break;
|
||||
case nct6791:
|
||||
case nct6792:
|
||||
data->in_num = 15;
|
||||
data->pwm_num = 6;
|
||||
data->auto_pwm_num = 4;
|
||||
|
@ -3650,12 +3681,20 @@ static int nct6775_probe(struct platform_device *pdev)
|
|||
data->REG_WEIGHT_TEMP[1] = NCT6791_REG_WEIGHT_TEMP_STEP_TOL;
|
||||
data->REG_WEIGHT_TEMP[2] = NCT6791_REG_WEIGHT_TEMP_BASE;
|
||||
data->REG_ALARM = NCT6791_REG_ALARM;
|
||||
data->REG_BEEP = NCT6776_REG_BEEP;
|
||||
if (data->kind == nct6791)
|
||||
data->REG_BEEP = NCT6776_REG_BEEP;
|
||||
else
|
||||
data->REG_BEEP = NCT6792_REG_BEEP;
|
||||
|
||||
reg_temp = NCT6779_REG_TEMP;
|
||||
reg_temp_mon = NCT6779_REG_TEMP_MON;
|
||||
num_reg_temp = ARRAY_SIZE(NCT6779_REG_TEMP);
|
||||
num_reg_temp_mon = ARRAY_SIZE(NCT6779_REG_TEMP_MON);
|
||||
if (data->kind == nct6791) {
|
||||
reg_temp_mon = NCT6779_REG_TEMP_MON;
|
||||
num_reg_temp_mon = ARRAY_SIZE(NCT6779_REG_TEMP_MON);
|
||||
} else {
|
||||
reg_temp_mon = NCT6792_REG_TEMP_MON;
|
||||
num_reg_temp_mon = ARRAY_SIZE(NCT6792_REG_TEMP_MON);
|
||||
}
|
||||
reg_temp_over = NCT6779_REG_TEMP_OVER;
|
||||
reg_temp_hyst = NCT6779_REG_TEMP_HYST;
|
||||
reg_temp_config = NCT6779_REG_TEMP_CONFIG;
|
||||
|
@ -3854,6 +3893,7 @@ static int nct6775_probe(struct platform_device *pdev)
|
|||
case nct6106:
|
||||
case nct6779:
|
||||
case nct6791:
|
||||
case nct6792:
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -3885,6 +3925,7 @@ static int nct6775_probe(struct platform_device *pdev)
|
|||
tmp |= 0x3e;
|
||||
break;
|
||||
case nct6791:
|
||||
case nct6792:
|
||||
tmp |= 0x7e;
|
||||
break;
|
||||
}
|
||||
|
@ -3972,7 +4013,7 @@ static int nct6775_resume(struct device *dev)
|
|||
mutex_lock(&data->update_lock);
|
||||
data->bank = 0xff; /* Force initial bank selection */
|
||||
|
||||
if (data->kind == nct6791) {
|
||||
if (data->kind == nct6791 || data->kind == nct6792) {
|
||||
err = superio_enter(data->sioreg);
|
||||
if (err)
|
||||
goto abort;
|
||||
|
@ -4052,6 +4093,7 @@ static const char * const nct6775_sio_names[] __initconst = {
|
|||
"NCT6776D/F",
|
||||
"NCT6779D",
|
||||
"NCT6791D",
|
||||
"NCT6792D",
|
||||
};
|
||||
|
||||
/* nct6775_find() looks for a '627 in the Super-I/O config space */
|
||||
|
@ -4086,6 +4128,9 @@ static int __init nct6775_find(int sioaddr, struct nct6775_sio_data *sio_data)
|
|||
case SIO_NCT6791_ID:
|
||||
sio_data->kind = nct6791;
|
||||
break;
|
||||
case SIO_NCT6792_ID:
|
||||
sio_data->kind = nct6792;
|
||||
break;
|
||||
default:
|
||||
if (val != 0xffff)
|
||||
pr_debug("unsupported chip ID: 0x%04x\n", val);
|
||||
|
@ -4111,7 +4156,7 @@ static int __init nct6775_find(int sioaddr, struct nct6775_sio_data *sio_data)
|
|||
superio_outb(sioaddr, SIO_REG_ENABLE, val | 0x01);
|
||||
}
|
||||
|
||||
if (sio_data->kind == nct6791)
|
||||
if (sio_data->kind == nct6791 || sio_data->kind == nct6792)
|
||||
nct6791_enable_io_mapping(sioaddr);
|
||||
|
||||
superio_exit(sioaddr);
|
||||
|
@ -4221,7 +4266,7 @@ static void __exit sensors_nct6775_exit(void)
|
|||
}
|
||||
|
||||
MODULE_AUTHOR("Guenter Roeck <linux@roeck-us.net>");
|
||||
MODULE_DESCRIPTION("NCT6106D/NCT6775F/NCT6776F/NCT6779D/NCT6791D driver");
|
||||
MODULE_DESCRIPTION("NCT6106D/NCT6775F/NCT6776F/NCT6779D/NCT6791D/NCT6792D driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
module_init(sensors_nct6775_init);
|
||||
|
|
|
@ -0,0 +1,860 @@
|
|||
/*
|
||||
* nct7802 - Driver for Nuvoton NCT7802Y
|
||||
*
|
||||
* Copyright (C) 2014 Guenter Roeck <linux@roeck-us.net>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
||||
|
||||
#include <linux/err.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/hwmon.h>
|
||||
#include <linux/hwmon-sysfs.h>
|
||||
#include <linux/jiffies.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#define DRVNAME "nct7802"
|
||||
|
||||
static const u8 REG_VOLTAGE[5] = { 0x09, 0x0a, 0x0c, 0x0d, 0x0e };
|
||||
|
||||
static const u8 REG_VOLTAGE_LIMIT_LSB[2][5] = {
|
||||
{ 0x40, 0x00, 0x42, 0x44, 0x46 },
|
||||
{ 0x3f, 0x00, 0x41, 0x43, 0x45 },
|
||||
};
|
||||
|
||||
static const u8 REG_VOLTAGE_LIMIT_MSB[5] = { 0x48, 0x00, 0x47, 0x47, 0x48 };
|
||||
|
||||
static const u8 REG_VOLTAGE_LIMIT_MSB_SHIFT[2][5] = {
|
||||
{ 0, 0, 4, 0, 4 },
|
||||
{ 2, 0, 6, 2, 6 },
|
||||
};
|
||||
|
||||
#define REG_BANK 0x00
|
||||
#define REG_TEMP_LSB 0x05
|
||||
#define REG_TEMP_PECI_LSB 0x08
|
||||
#define REG_VOLTAGE_LOW 0x0f
|
||||
#define REG_FANCOUNT_LOW 0x13
|
||||
#define REG_START 0x21
|
||||
#define REG_MODE 0x22
|
||||
#define REG_PECI_ENABLE 0x23
|
||||
#define REG_FAN_ENABLE 0x24
|
||||
#define REG_VMON_ENABLE 0x25
|
||||
#define REG_VENDOR_ID 0xfd
|
||||
#define REG_CHIP_ID 0xfe
|
||||
#define REG_VERSION_ID 0xff
|
||||
|
||||
/*
|
||||
* Data structures and manipulation thereof
|
||||
*/
|
||||
|
||||
struct nct7802_data {
|
||||
struct regmap *regmap;
|
||||
struct mutex access_lock; /* for multi-byte read and write operations */
|
||||
};
|
||||
|
||||
static int nct7802_read_temp(struct nct7802_data *data,
|
||||
u8 reg_temp, u8 reg_temp_low, int *temp)
|
||||
{
|
||||
unsigned int t1, t2 = 0;
|
||||
int err;
|
||||
|
||||
*temp = 0;
|
||||
|
||||
mutex_lock(&data->access_lock);
|
||||
err = regmap_read(data->regmap, reg_temp, &t1);
|
||||
if (err < 0)
|
||||
goto abort;
|
||||
t1 <<= 8;
|
||||
if (reg_temp_low) { /* 11 bit data */
|
||||
err = regmap_read(data->regmap, reg_temp_low, &t2);
|
||||
if (err < 0)
|
||||
goto abort;
|
||||
}
|
||||
t1 |= t2 & 0xe0;
|
||||
*temp = (s16)t1 / 32 * 125;
|
||||
abort:
|
||||
mutex_unlock(&data->access_lock);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int nct7802_read_fan(struct nct7802_data *data, u8 reg_fan)
|
||||
{
|
||||
unsigned int f1, f2;
|
||||
int ret;
|
||||
|
||||
mutex_lock(&data->access_lock);
|
||||
ret = regmap_read(data->regmap, reg_fan, &f1);
|
||||
if (ret < 0)
|
||||
goto abort;
|
||||
ret = regmap_read(data->regmap, REG_FANCOUNT_LOW, &f2);
|
||||
if (ret < 0)
|
||||
goto abort;
|
||||
ret = (f1 << 5) | (f2 >> 3);
|
||||
/* convert fan count to rpm */
|
||||
if (ret == 0x1fff) /* maximum value, assume fan is stopped */
|
||||
ret = 0;
|
||||
else if (ret)
|
||||
ret = DIV_ROUND_CLOSEST(1350000U, ret);
|
||||
abort:
|
||||
mutex_unlock(&data->access_lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int nct7802_read_fan_min(struct nct7802_data *data, u8 reg_fan_low,
|
||||
u8 reg_fan_high)
|
||||
{
|
||||
unsigned int f1, f2;
|
||||
int ret;
|
||||
|
||||
mutex_lock(&data->access_lock);
|
||||
ret = regmap_read(data->regmap, reg_fan_low, &f1);
|
||||
if (ret < 0)
|
||||
goto abort;
|
||||
ret = regmap_read(data->regmap, reg_fan_high, &f2);
|
||||
if (ret < 0)
|
||||
goto abort;
|
||||
ret = f1 | ((f2 & 0xf8) << 5);
|
||||
/* convert fan count to rpm */
|
||||
if (ret == 0x1fff) /* maximum value, assume no limit */
|
||||
ret = 0;
|
||||
else if (ret)
|
||||
ret = DIV_ROUND_CLOSEST(1350000U, ret);
|
||||
abort:
|
||||
mutex_unlock(&data->access_lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int nct7802_write_fan_min(struct nct7802_data *data, u8 reg_fan_low,
|
||||
u8 reg_fan_high, unsigned int limit)
|
||||
{
|
||||
int err;
|
||||
|
||||
if (limit)
|
||||
limit = DIV_ROUND_CLOSEST(1350000U, limit);
|
||||
else
|
||||
limit = 0x1fff;
|
||||
limit = clamp_val(limit, 0, 0x1fff);
|
||||
|
||||
mutex_lock(&data->access_lock);
|
||||
err = regmap_write(data->regmap, reg_fan_low, limit & 0xff);
|
||||
if (err < 0)
|
||||
goto abort;
|
||||
|
||||
err = regmap_write(data->regmap, reg_fan_high, (limit & 0x1f00) >> 5);
|
||||
abort:
|
||||
mutex_unlock(&data->access_lock);
|
||||
return err;
|
||||
}
|
||||
|
||||
static u8 nct7802_vmul[] = { 4, 2, 2, 2, 2 };
|
||||
|
||||
static int nct7802_read_voltage(struct nct7802_data *data, int nr, int index)
|
||||
{
|
||||
unsigned int v1, v2;
|
||||
int ret;
|
||||
|
||||
mutex_lock(&data->access_lock);
|
||||
if (index == 0) { /* voltage */
|
||||
ret = regmap_read(data->regmap, REG_VOLTAGE[nr], &v1);
|
||||
if (ret < 0)
|
||||
goto abort;
|
||||
ret = regmap_read(data->regmap, REG_VOLTAGE_LOW, &v2);
|
||||
if (ret < 0)
|
||||
goto abort;
|
||||
ret = ((v1 << 2) | (v2 >> 6)) * nct7802_vmul[nr];
|
||||
} else { /* limit */
|
||||
int shift = 8 - REG_VOLTAGE_LIMIT_MSB_SHIFT[index - 1][nr];
|
||||
|
||||
ret = regmap_read(data->regmap,
|
||||
REG_VOLTAGE_LIMIT_LSB[index - 1][nr], &v1);
|
||||
if (ret < 0)
|
||||
goto abort;
|
||||
ret = regmap_read(data->regmap, REG_VOLTAGE_LIMIT_MSB[nr],
|
||||
&v2);
|
||||
if (ret < 0)
|
||||
goto abort;
|
||||
ret = (v1 | ((v2 << shift) & 0x300)) * nct7802_vmul[nr];
|
||||
}
|
||||
abort:
|
||||
mutex_unlock(&data->access_lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int nct7802_write_voltage(struct nct7802_data *data, int nr, int index,
|
||||
unsigned int voltage)
|
||||
{
|
||||
int shift = 8 - REG_VOLTAGE_LIMIT_MSB_SHIFT[index - 1][nr];
|
||||
int err;
|
||||
|
||||
voltage = DIV_ROUND_CLOSEST(voltage, nct7802_vmul[nr]);
|
||||
voltage = clamp_val(voltage, 0, 0x3ff);
|
||||
|
||||
mutex_lock(&data->access_lock);
|
||||
err = regmap_write(data->regmap,
|
||||
REG_VOLTAGE_LIMIT_LSB[index - 1][nr],
|
||||
voltage & 0xff);
|
||||
if (err < 0)
|
||||
goto abort;
|
||||
|
||||
err = regmap_update_bits(data->regmap, REG_VOLTAGE_LIMIT_MSB[nr],
|
||||
0x0300 >> shift, (voltage & 0x0300) >> shift);
|
||||
abort:
|
||||
mutex_unlock(&data->access_lock);
|
||||
return err;
|
||||
}
|
||||
|
||||
static ssize_t show_in(struct device *dev, struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr);
|
||||
struct nct7802_data *data = dev_get_drvdata(dev);
|
||||
int voltage;
|
||||
|
||||
voltage = nct7802_read_voltage(data, sattr->nr, sattr->index);
|
||||
if (voltage < 0)
|
||||
return voltage;
|
||||
|
||||
return sprintf(buf, "%d\n", voltage);
|
||||
}
|
||||
|
||||
static ssize_t store_in(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 nct7802_data *data = dev_get_drvdata(dev);
|
||||
int index = sattr->index;
|
||||
int nr = sattr->nr;
|
||||
unsigned long val;
|
||||
int err;
|
||||
|
||||
err = kstrtoul(buf, 10, &val);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
err = nct7802_write_voltage(data, nr, index, val);
|
||||
return err ? : count;
|
||||
}
|
||||
|
||||
static ssize_t show_temp(struct device *dev, struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct nct7802_data *data = dev_get_drvdata(dev);
|
||||
struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr);
|
||||
int err, temp;
|
||||
|
||||
err = nct7802_read_temp(data, sattr->nr, sattr->index, &temp);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
return sprintf(buf, "%d\n", temp);
|
||||
}
|
||||
|
||||
static ssize_t store_temp(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 nct7802_data *data = dev_get_drvdata(dev);
|
||||
int nr = sattr->nr;
|
||||
long val;
|
||||
int err;
|
||||
|
||||
err = kstrtol(buf, 10, &val);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
val = clamp_val(DIV_ROUND_CLOSEST(val, 1000), -128, 127);
|
||||
|
||||
err = regmap_write(data->regmap, nr, val & 0xff);
|
||||
return err ? : count;
|
||||
}
|
||||
|
||||
static ssize_t show_fan(struct device *dev, struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr);
|
||||
struct nct7802_data *data = dev_get_drvdata(dev);
|
||||
int speed;
|
||||
|
||||
speed = nct7802_read_fan(data, sattr->index);
|
||||
if (speed < 0)
|
||||
return speed;
|
||||
|
||||
return sprintf(buf, "%d\n", speed);
|
||||
}
|
||||
|
||||
static ssize_t show_fan_min(struct device *dev, struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr);
|
||||
struct nct7802_data *data = dev_get_drvdata(dev);
|
||||
int speed;
|
||||
|
||||
speed = nct7802_read_fan_min(data, sattr->nr, sattr->index);
|
||||
if (speed < 0)
|
||||
return speed;
|
||||
|
||||
return sprintf(buf, "%d\n", speed);
|
||||
}
|
||||
|
||||
static ssize_t store_fan_min(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 nct7802_data *data = dev_get_drvdata(dev);
|
||||
unsigned long val;
|
||||
int err;
|
||||
|
||||
err = kstrtoul(buf, 10, &val);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
err = nct7802_write_fan_min(data, sattr->nr, sattr->index, val);
|
||||
return err ? : count;
|
||||
}
|
||||
|
||||
static ssize_t show_alarm(struct device *dev, struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct nct7802_data *data = dev_get_drvdata(dev);
|
||||
struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr);
|
||||
int bit = sattr->index;
|
||||
unsigned int val;
|
||||
int ret;
|
||||
|
||||
ret = regmap_read(data->regmap, sattr->nr, &val);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return sprintf(buf, "%u\n", !!(val & (1 << bit)));
|
||||
}
|
||||
|
||||
static ssize_t
|
||||
show_beep(struct device *dev, struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr);
|
||||
struct nct7802_data *data = dev_get_drvdata(dev);
|
||||
unsigned int regval;
|
||||
int err;
|
||||
|
||||
err = regmap_read(data->regmap, sattr->nr, ®val);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
return sprintf(buf, "%u\n", !!(regval & (1 << sattr->index)));
|
||||
}
|
||||
|
||||
static ssize_t
|
||||
store_beep(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 nct7802_data *data = dev_get_drvdata(dev);
|
||||
unsigned long val;
|
||||
int err;
|
||||
|
||||
err = kstrtoul(buf, 10, &val);
|
||||
if (err < 0)
|
||||
return err;
|
||||
if (val > 1)
|
||||
return -EINVAL;
|
||||
|
||||
err = regmap_update_bits(data->regmap, sattr->nr, 1 << sattr->index,
|
||||
val ? 1 << sattr->index : 0);
|
||||
return err ? : count;
|
||||
}
|
||||
|
||||
static SENSOR_DEVICE_ATTR_2(temp1_input, S_IRUGO, show_temp, NULL, 0x01,
|
||||
REG_TEMP_LSB);
|
||||
static SENSOR_DEVICE_ATTR_2(temp1_min, S_IRUGO | S_IWUSR, show_temp,
|
||||
store_temp, 0x31, 0);
|
||||
static SENSOR_DEVICE_ATTR_2(temp1_max, S_IRUGO | S_IWUSR, show_temp,
|
||||
store_temp, 0x30, 0);
|
||||
static SENSOR_DEVICE_ATTR_2(temp1_crit, S_IRUGO | S_IWUSR, show_temp,
|
||||
store_temp, 0x3a, 0);
|
||||
|
||||
static SENSOR_DEVICE_ATTR_2(temp2_input, S_IRUGO, show_temp, NULL, 0x02,
|
||||
REG_TEMP_LSB);
|
||||
static SENSOR_DEVICE_ATTR_2(temp2_min, S_IRUGO | S_IWUSR, show_temp,
|
||||
store_temp, 0x33, 0);
|
||||
static SENSOR_DEVICE_ATTR_2(temp2_max, S_IRUGO | S_IWUSR, show_temp,
|
||||
store_temp, 0x32, 0);
|
||||
static SENSOR_DEVICE_ATTR_2(temp2_crit, S_IRUGO | S_IWUSR, show_temp,
|
||||
store_temp, 0x3b, 0);
|
||||
|
||||
static SENSOR_DEVICE_ATTR_2(temp3_input, S_IRUGO, show_temp, NULL, 0x03,
|
||||
REG_TEMP_LSB);
|
||||
static SENSOR_DEVICE_ATTR_2(temp3_min, S_IRUGO | S_IWUSR, show_temp,
|
||||
store_temp, 0x35, 0);
|
||||
static SENSOR_DEVICE_ATTR_2(temp3_max, S_IRUGO | S_IWUSR, show_temp,
|
||||
store_temp, 0x34, 0);
|
||||
static SENSOR_DEVICE_ATTR_2(temp3_crit, S_IRUGO | S_IWUSR, show_temp,
|
||||
store_temp, 0x3c, 0);
|
||||
|
||||
static SENSOR_DEVICE_ATTR_2(temp4_input, S_IRUGO, show_temp, NULL, 0x04, 0);
|
||||
static SENSOR_DEVICE_ATTR_2(temp4_min, S_IRUGO | S_IWUSR, show_temp,
|
||||
store_temp, 0x37, 0);
|
||||
static SENSOR_DEVICE_ATTR_2(temp4_max, S_IRUGO | S_IWUSR, show_temp,
|
||||
store_temp, 0x36, 0);
|
||||
static SENSOR_DEVICE_ATTR_2(temp4_crit, S_IRUGO | S_IWUSR, show_temp,
|
||||
store_temp, 0x3d, 0);
|
||||
|
||||
static SENSOR_DEVICE_ATTR_2(temp5_input, S_IRUGO, show_temp, NULL, 0x06,
|
||||
REG_TEMP_PECI_LSB);
|
||||
static SENSOR_DEVICE_ATTR_2(temp5_min, S_IRUGO | S_IWUSR, show_temp,
|
||||
store_temp, 0x39, 0);
|
||||
static SENSOR_DEVICE_ATTR_2(temp5_max, S_IRUGO | S_IWUSR, show_temp,
|
||||
store_temp, 0x38, 0);
|
||||
static SENSOR_DEVICE_ATTR_2(temp5_crit, S_IRUGO | S_IWUSR, show_temp,
|
||||
store_temp, 0x3e, 0);
|
||||
|
||||
static SENSOR_DEVICE_ATTR_2(temp6_input, S_IRUGO, show_temp, NULL, 0x07,
|
||||
REG_TEMP_PECI_LSB);
|
||||
|
||||
static SENSOR_DEVICE_ATTR_2(temp1_min_alarm, S_IRUGO, show_alarm, NULL,
|
||||
0x18, 0);
|
||||
static SENSOR_DEVICE_ATTR_2(temp2_min_alarm, S_IRUGO, show_alarm, NULL,
|
||||
0x18, 1);
|
||||
static SENSOR_DEVICE_ATTR_2(temp3_min_alarm, S_IRUGO, show_alarm, NULL,
|
||||
0x18, 2);
|
||||
static SENSOR_DEVICE_ATTR_2(temp4_min_alarm, S_IRUGO, show_alarm, NULL,
|
||||
0x18, 3);
|
||||
static SENSOR_DEVICE_ATTR_2(temp5_min_alarm, S_IRUGO, show_alarm, NULL,
|
||||
0x18, 4);
|
||||
|
||||
static SENSOR_DEVICE_ATTR_2(temp1_max_alarm, S_IRUGO, show_alarm, NULL,
|
||||
0x19, 0);
|
||||
static SENSOR_DEVICE_ATTR_2(temp2_max_alarm, S_IRUGO, show_alarm, NULL,
|
||||
0x19, 1);
|
||||
static SENSOR_DEVICE_ATTR_2(temp3_max_alarm, S_IRUGO, show_alarm, NULL,
|
||||
0x19, 2);
|
||||
static SENSOR_DEVICE_ATTR_2(temp4_max_alarm, S_IRUGO, show_alarm, NULL,
|
||||
0x19, 3);
|
||||
static SENSOR_DEVICE_ATTR_2(temp5_max_alarm, S_IRUGO, show_alarm, NULL,
|
||||
0x19, 4);
|
||||
|
||||
static SENSOR_DEVICE_ATTR_2(temp1_crit_alarm, S_IRUGO, show_alarm, NULL,
|
||||
0x1b, 0);
|
||||
static SENSOR_DEVICE_ATTR_2(temp2_crit_alarm, S_IRUGO, show_alarm, NULL,
|
||||
0x1b, 1);
|
||||
static SENSOR_DEVICE_ATTR_2(temp3_crit_alarm, S_IRUGO, show_alarm, NULL,
|
||||
0x1b, 2);
|
||||
static SENSOR_DEVICE_ATTR_2(temp4_crit_alarm, S_IRUGO, show_alarm, NULL,
|
||||
0x1b, 3);
|
||||
static SENSOR_DEVICE_ATTR_2(temp5_crit_alarm, S_IRUGO, show_alarm, NULL,
|
||||
0x1b, 4);
|
||||
|
||||
static SENSOR_DEVICE_ATTR_2(temp1_fault, S_IRUGO, show_alarm, NULL, 0x17, 0);
|
||||
static SENSOR_DEVICE_ATTR_2(temp2_fault, S_IRUGO, show_alarm, NULL, 0x17, 1);
|
||||
static SENSOR_DEVICE_ATTR_2(temp3_fault, S_IRUGO, show_alarm, NULL, 0x17, 2);
|
||||
|
||||
static SENSOR_DEVICE_ATTR_2(temp1_beep, S_IRUGO | S_IWUSR, show_beep,
|
||||
store_beep, 0x5c, 0);
|
||||
static SENSOR_DEVICE_ATTR_2(temp2_beep, S_IRUGO | S_IWUSR, show_beep,
|
||||
store_beep, 0x5c, 1);
|
||||
static SENSOR_DEVICE_ATTR_2(temp3_beep, S_IRUGO | S_IWUSR, show_beep,
|
||||
store_beep, 0x5c, 2);
|
||||
static SENSOR_DEVICE_ATTR_2(temp4_beep, S_IRUGO | S_IWUSR, show_beep,
|
||||
store_beep, 0x5c, 3);
|
||||
static SENSOR_DEVICE_ATTR_2(temp5_beep, S_IRUGO | S_IWUSR, show_beep,
|
||||
store_beep, 0x5c, 4);
|
||||
static SENSOR_DEVICE_ATTR_2(temp6_beep, S_IRUGO | S_IWUSR, show_beep,
|
||||
store_beep, 0x5c, 5);
|
||||
|
||||
static struct attribute *nct7802_temp_attrs[] = {
|
||||
&sensor_dev_attr_temp1_input.dev_attr.attr,
|
||||
&sensor_dev_attr_temp1_min.dev_attr.attr,
|
||||
&sensor_dev_attr_temp1_max.dev_attr.attr,
|
||||
&sensor_dev_attr_temp1_crit.dev_attr.attr,
|
||||
&sensor_dev_attr_temp1_min_alarm.dev_attr.attr,
|
||||
&sensor_dev_attr_temp1_max_alarm.dev_attr.attr,
|
||||
&sensor_dev_attr_temp1_crit_alarm.dev_attr.attr,
|
||||
&sensor_dev_attr_temp1_fault.dev_attr.attr,
|
||||
&sensor_dev_attr_temp1_beep.dev_attr.attr,
|
||||
|
||||
&sensor_dev_attr_temp2_input.dev_attr.attr, /* 9 */
|
||||
&sensor_dev_attr_temp2_min.dev_attr.attr,
|
||||
&sensor_dev_attr_temp2_max.dev_attr.attr,
|
||||
&sensor_dev_attr_temp2_crit.dev_attr.attr,
|
||||
&sensor_dev_attr_temp2_min_alarm.dev_attr.attr,
|
||||
&sensor_dev_attr_temp2_max_alarm.dev_attr.attr,
|
||||
&sensor_dev_attr_temp2_crit_alarm.dev_attr.attr,
|
||||
&sensor_dev_attr_temp2_fault.dev_attr.attr,
|
||||
&sensor_dev_attr_temp2_beep.dev_attr.attr,
|
||||
|
||||
&sensor_dev_attr_temp3_input.dev_attr.attr, /* 18 */
|
||||
&sensor_dev_attr_temp3_min.dev_attr.attr,
|
||||
&sensor_dev_attr_temp3_max.dev_attr.attr,
|
||||
&sensor_dev_attr_temp3_crit.dev_attr.attr,
|
||||
&sensor_dev_attr_temp3_min_alarm.dev_attr.attr,
|
||||
&sensor_dev_attr_temp3_max_alarm.dev_attr.attr,
|
||||
&sensor_dev_attr_temp3_crit_alarm.dev_attr.attr,
|
||||
&sensor_dev_attr_temp3_fault.dev_attr.attr,
|
||||
&sensor_dev_attr_temp3_beep.dev_attr.attr,
|
||||
|
||||
&sensor_dev_attr_temp4_input.dev_attr.attr, /* 27 */
|
||||
&sensor_dev_attr_temp4_min.dev_attr.attr,
|
||||
&sensor_dev_attr_temp4_max.dev_attr.attr,
|
||||
&sensor_dev_attr_temp4_crit.dev_attr.attr,
|
||||
&sensor_dev_attr_temp4_min_alarm.dev_attr.attr,
|
||||
&sensor_dev_attr_temp4_max_alarm.dev_attr.attr,
|
||||
&sensor_dev_attr_temp4_crit_alarm.dev_attr.attr,
|
||||
&sensor_dev_attr_temp4_beep.dev_attr.attr,
|
||||
|
||||
&sensor_dev_attr_temp5_input.dev_attr.attr, /* 35 */
|
||||
&sensor_dev_attr_temp5_min.dev_attr.attr,
|
||||
&sensor_dev_attr_temp5_max.dev_attr.attr,
|
||||
&sensor_dev_attr_temp5_crit.dev_attr.attr,
|
||||
&sensor_dev_attr_temp5_min_alarm.dev_attr.attr,
|
||||
&sensor_dev_attr_temp5_max_alarm.dev_attr.attr,
|
||||
&sensor_dev_attr_temp5_crit_alarm.dev_attr.attr,
|
||||
&sensor_dev_attr_temp5_beep.dev_attr.attr,
|
||||
|
||||
&sensor_dev_attr_temp6_input.dev_attr.attr, /* 43 */
|
||||
&sensor_dev_attr_temp6_beep.dev_attr.attr,
|
||||
|
||||
NULL
|
||||
};
|
||||
|
||||
static umode_t nct7802_temp_is_visible(struct kobject *kobj,
|
||||
struct attribute *attr, int index)
|
||||
{
|
||||
struct device *dev = container_of(kobj, struct device, kobj);
|
||||
struct nct7802_data *data = dev_get_drvdata(dev);
|
||||
unsigned int reg;
|
||||
int err;
|
||||
|
||||
err = regmap_read(data->regmap, REG_MODE, ®);
|
||||
if (err < 0)
|
||||
return 0;
|
||||
|
||||
if (index < 9 &&
|
||||
(reg & 03) != 0x01 && (reg & 0x03) != 0x02) /* RD1 */
|
||||
return 0;
|
||||
if (index >= 9 && index < 18 &&
|
||||
(reg & 0x0c) != 0x04 && (reg & 0x0c) != 0x08) /* RD2 */
|
||||
return 0;
|
||||
if (index >= 18 && index < 27 && (reg & 0x30) != 0x10) /* RD3 */
|
||||
return 0;
|
||||
if (index >= 27 && index < 35) /* local */
|
||||
return attr->mode;
|
||||
|
||||
err = regmap_read(data->regmap, REG_PECI_ENABLE, ®);
|
||||
if (err < 0)
|
||||
return 0;
|
||||
|
||||
if (index >= 35 && index < 43 && !(reg & 0x01)) /* PECI 0 */
|
||||
return 0;
|
||||
|
||||
if (index >= 0x43 && (!(reg & 0x02))) /* PECI 1 */
|
||||
return 0;
|
||||
|
||||
return attr->mode;
|
||||
}
|
||||
|
||||
static struct attribute_group nct7802_temp_group = {
|
||||
.attrs = nct7802_temp_attrs,
|
||||
.is_visible = nct7802_temp_is_visible,
|
||||
};
|
||||
|
||||
static SENSOR_DEVICE_ATTR_2(in0_input, S_IRUGO, show_in, NULL, 0, 0);
|
||||
static SENSOR_DEVICE_ATTR_2(in0_min, S_IRUGO | S_IWUSR, show_in, store_in,
|
||||
0, 1);
|
||||
static SENSOR_DEVICE_ATTR_2(in0_max, S_IRUGO | S_IWUSR, show_in, store_in,
|
||||
0, 2);
|
||||
static SENSOR_DEVICE_ATTR_2(in0_alarm, S_IRUGO, show_alarm, NULL, 0x1e, 3);
|
||||
static SENSOR_DEVICE_ATTR_2(in0_beep, S_IRUGO | S_IWUSR, show_beep, store_beep,
|
||||
0x5a, 3);
|
||||
|
||||
static SENSOR_DEVICE_ATTR_2(in1_input, S_IRUGO, show_in, NULL, 1, 0);
|
||||
|
||||
static SENSOR_DEVICE_ATTR_2(in2_input, S_IRUGO, show_in, NULL, 2, 0);
|
||||
static SENSOR_DEVICE_ATTR_2(in2_min, S_IRUGO | S_IWUSR, show_in, store_in,
|
||||
2, 1);
|
||||
static SENSOR_DEVICE_ATTR_2(in2_max, S_IRUGO | S_IWUSR, show_in, store_in,
|
||||
2, 2);
|
||||
static SENSOR_DEVICE_ATTR_2(in2_alarm, S_IRUGO, show_alarm, NULL, 0x1e, 0);
|
||||
static SENSOR_DEVICE_ATTR_2(in2_beep, S_IRUGO | S_IWUSR, show_beep, store_beep,
|
||||
0x5a, 0);
|
||||
|
||||
static SENSOR_DEVICE_ATTR_2(in3_input, S_IRUGO, show_in, NULL, 3, 0);
|
||||
static SENSOR_DEVICE_ATTR_2(in3_min, S_IRUGO | S_IWUSR, show_in, store_in,
|
||||
3, 1);
|
||||
static SENSOR_DEVICE_ATTR_2(in3_max, S_IRUGO | S_IWUSR, show_in, store_in,
|
||||
3, 2);
|
||||
static SENSOR_DEVICE_ATTR_2(in3_alarm, S_IRUGO, show_alarm, NULL, 0x1e, 1);
|
||||
static SENSOR_DEVICE_ATTR_2(in3_beep, S_IRUGO | S_IWUSR, show_beep, store_beep,
|
||||
0x5a, 1);
|
||||
|
||||
static SENSOR_DEVICE_ATTR_2(in4_input, S_IRUGO, show_in, NULL, 4, 0);
|
||||
static SENSOR_DEVICE_ATTR_2(in4_min, S_IRUGO | S_IWUSR, show_in, store_in,
|
||||
4, 1);
|
||||
static SENSOR_DEVICE_ATTR_2(in4_max, S_IRUGO | S_IWUSR, show_in, store_in,
|
||||
4, 2);
|
||||
static SENSOR_DEVICE_ATTR_2(in4_alarm, S_IRUGO, show_alarm, NULL, 0x1e, 2);
|
||||
static SENSOR_DEVICE_ATTR_2(in4_beep, S_IRUGO | S_IWUSR, show_beep, store_beep,
|
||||
0x5a, 2);
|
||||
|
||||
static struct attribute *nct7802_in_attrs[] = {
|
||||
&sensor_dev_attr_in0_input.dev_attr.attr,
|
||||
&sensor_dev_attr_in0_min.dev_attr.attr,
|
||||
&sensor_dev_attr_in0_max.dev_attr.attr,
|
||||
&sensor_dev_attr_in0_alarm.dev_attr.attr,
|
||||
&sensor_dev_attr_in0_beep.dev_attr.attr,
|
||||
|
||||
&sensor_dev_attr_in1_input.dev_attr.attr, /* 5 */
|
||||
|
||||
&sensor_dev_attr_in2_input.dev_attr.attr, /* 6 */
|
||||
&sensor_dev_attr_in2_min.dev_attr.attr,
|
||||
&sensor_dev_attr_in2_max.dev_attr.attr,
|
||||
&sensor_dev_attr_in2_alarm.dev_attr.attr,
|
||||
&sensor_dev_attr_in2_beep.dev_attr.attr,
|
||||
|
||||
&sensor_dev_attr_in3_input.dev_attr.attr, /* 11 */
|
||||
&sensor_dev_attr_in3_min.dev_attr.attr,
|
||||
&sensor_dev_attr_in3_max.dev_attr.attr,
|
||||
&sensor_dev_attr_in3_alarm.dev_attr.attr,
|
||||
&sensor_dev_attr_in3_beep.dev_attr.attr,
|
||||
|
||||
&sensor_dev_attr_in4_input.dev_attr.attr, /* 17 */
|
||||
&sensor_dev_attr_in4_min.dev_attr.attr,
|
||||
&sensor_dev_attr_in4_max.dev_attr.attr,
|
||||
&sensor_dev_attr_in4_alarm.dev_attr.attr,
|
||||
&sensor_dev_attr_in4_beep.dev_attr.attr,
|
||||
|
||||
NULL,
|
||||
};
|
||||
|
||||
static umode_t nct7802_in_is_visible(struct kobject *kobj,
|
||||
struct attribute *attr, int index)
|
||||
{
|
||||
struct device *dev = container_of(kobj, struct device, kobj);
|
||||
struct nct7802_data *data = dev_get_drvdata(dev);
|
||||
unsigned int reg;
|
||||
int err;
|
||||
|
||||
if (index < 6) /* VCC, VCORE */
|
||||
return attr->mode;
|
||||
|
||||
err = regmap_read(data->regmap, REG_MODE, ®);
|
||||
if (err < 0)
|
||||
return 0;
|
||||
|
||||
if (index >= 6 && index < 11 && (reg & 0x03) != 0x03) /* VSEN1 */
|
||||
return 0;
|
||||
if (index >= 11 && index < 17 && (reg & 0x0c) != 0x0c) /* VSEN2 */
|
||||
return 0;
|
||||
if (index >= 17 && (reg & 0x30) != 0x30) /* VSEN3 */
|
||||
return 0;
|
||||
|
||||
return attr->mode;
|
||||
}
|
||||
|
||||
static struct attribute_group nct7802_in_group = {
|
||||
.attrs = nct7802_in_attrs,
|
||||
.is_visible = nct7802_in_is_visible,
|
||||
};
|
||||
|
||||
static SENSOR_DEVICE_ATTR(fan1_input, S_IRUGO, show_fan, NULL, 0x10);
|
||||
static SENSOR_DEVICE_ATTR_2(fan1_min, S_IRUGO | S_IWUSR, show_fan_min,
|
||||
store_fan_min, 0x49, 0x4c);
|
||||
static SENSOR_DEVICE_ATTR_2(fan1_alarm, S_IRUGO, show_alarm, NULL, 0x1a, 0);
|
||||
static SENSOR_DEVICE_ATTR_2(fan1_beep, S_IRUGO | S_IWUSR, show_beep, store_beep,
|
||||
0x5b, 0);
|
||||
static SENSOR_DEVICE_ATTR(fan2_input, S_IRUGO, show_fan, NULL, 0x11);
|
||||
static SENSOR_DEVICE_ATTR_2(fan2_min, S_IRUGO | S_IWUSR, show_fan_min,
|
||||
store_fan_min, 0x4a, 0x4d);
|
||||
static SENSOR_DEVICE_ATTR_2(fan2_alarm, S_IRUGO, show_alarm, NULL, 0x1a, 1);
|
||||
static SENSOR_DEVICE_ATTR_2(fan2_beep, S_IRUGO | S_IWUSR, show_beep, store_beep,
|
||||
0x5b, 1);
|
||||
static SENSOR_DEVICE_ATTR(fan3_input, S_IRUGO, show_fan, NULL, 0x12);
|
||||
static SENSOR_DEVICE_ATTR_2(fan3_min, S_IRUGO | S_IWUSR, show_fan_min,
|
||||
store_fan_min, 0x4b, 0x4e);
|
||||
static SENSOR_DEVICE_ATTR_2(fan3_alarm, S_IRUGO, show_alarm, NULL, 0x1a, 2);
|
||||
static SENSOR_DEVICE_ATTR_2(fan3_beep, S_IRUGO | S_IWUSR, show_beep, store_beep,
|
||||
0x5b, 2);
|
||||
|
||||
static struct attribute *nct7802_fan_attrs[] = {
|
||||
&sensor_dev_attr_fan1_input.dev_attr.attr,
|
||||
&sensor_dev_attr_fan1_min.dev_attr.attr,
|
||||
&sensor_dev_attr_fan1_alarm.dev_attr.attr,
|
||||
&sensor_dev_attr_fan1_beep.dev_attr.attr,
|
||||
&sensor_dev_attr_fan2_input.dev_attr.attr,
|
||||
&sensor_dev_attr_fan2_min.dev_attr.attr,
|
||||
&sensor_dev_attr_fan2_alarm.dev_attr.attr,
|
||||
&sensor_dev_attr_fan2_beep.dev_attr.attr,
|
||||
&sensor_dev_attr_fan3_input.dev_attr.attr,
|
||||
&sensor_dev_attr_fan3_min.dev_attr.attr,
|
||||
&sensor_dev_attr_fan3_alarm.dev_attr.attr,
|
||||
&sensor_dev_attr_fan3_beep.dev_attr.attr,
|
||||
|
||||
NULL
|
||||
};
|
||||
|
||||
static umode_t nct7802_fan_is_visible(struct kobject *kobj,
|
||||
struct attribute *attr, int index)
|
||||
{
|
||||
struct device *dev = container_of(kobj, struct device, kobj);
|
||||
struct nct7802_data *data = dev_get_drvdata(dev);
|
||||
int fan = index / 4; /* 4 attributes per fan */
|
||||
unsigned int reg;
|
||||
int err;
|
||||
|
||||
err = regmap_read(data->regmap, REG_FAN_ENABLE, ®);
|
||||
if (err < 0 || !(reg & (1 << fan)))
|
||||
return 0;
|
||||
|
||||
return attr->mode;
|
||||
}
|
||||
|
||||
static struct attribute_group nct7802_fan_group = {
|
||||
.attrs = nct7802_fan_attrs,
|
||||
.is_visible = nct7802_fan_is_visible,
|
||||
};
|
||||
|
||||
static const struct attribute_group *nct7802_groups[] = {
|
||||
&nct7802_temp_group,
|
||||
&nct7802_in_group,
|
||||
&nct7802_fan_group,
|
||||
NULL
|
||||
};
|
||||
|
||||
static int nct7802_detect(struct i2c_client *client,
|
||||
struct i2c_board_info *info)
|
||||
{
|
||||
int reg;
|
||||
|
||||
/*
|
||||
* Chip identification registers are only available in bank 0,
|
||||
* so only attempt chip detection if bank 0 is selected
|
||||
*/
|
||||
reg = i2c_smbus_read_byte_data(client, REG_BANK);
|
||||
if (reg != 0x00)
|
||||
return -ENODEV;
|
||||
|
||||
reg = i2c_smbus_read_byte_data(client, REG_VENDOR_ID);
|
||||
if (reg != 0x50)
|
||||
return -ENODEV;
|
||||
|
||||
reg = i2c_smbus_read_byte_data(client, REG_CHIP_ID);
|
||||
if (reg != 0xc3)
|
||||
return -ENODEV;
|
||||
|
||||
reg = i2c_smbus_read_byte_data(client, REG_VERSION_ID);
|
||||
if (reg < 0 || (reg & 0xf0) != 0x20)
|
||||
return -ENODEV;
|
||||
|
||||
/* Also validate lower bits of voltage and temperature registers */
|
||||
reg = i2c_smbus_read_byte_data(client, REG_TEMP_LSB);
|
||||
if (reg < 0 || (reg & 0x1f))
|
||||
return -ENODEV;
|
||||
|
||||
reg = i2c_smbus_read_byte_data(client, REG_TEMP_PECI_LSB);
|
||||
if (reg < 0 || (reg & 0x3f))
|
||||
return -ENODEV;
|
||||
|
||||
reg = i2c_smbus_read_byte_data(client, REG_VOLTAGE_LOW);
|
||||
if (reg < 0 || (reg & 0x3f))
|
||||
return -ENODEV;
|
||||
|
||||
strlcpy(info->type, "nct7802", I2C_NAME_SIZE);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool nct7802_regmap_is_volatile(struct device *dev, unsigned int reg)
|
||||
{
|
||||
return reg != REG_BANK && reg <= 0x20;
|
||||
}
|
||||
|
||||
static struct regmap_config nct7802_regmap_config = {
|
||||
.reg_bits = 8,
|
||||
.val_bits = 8,
|
||||
.cache_type = REGCACHE_RBTREE,
|
||||
.volatile_reg = nct7802_regmap_is_volatile,
|
||||
};
|
||||
|
||||
static int nct7802_init_chip(struct nct7802_data *data)
|
||||
{
|
||||
int err;
|
||||
|
||||
/* Enable ADC */
|
||||
err = regmap_update_bits(data->regmap, REG_START, 0x01, 0x01);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
/* Enable local temperature sensor */
|
||||
err = regmap_update_bits(data->regmap, REG_MODE, 0x40, 0x40);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
/* Enable Vcore and VCC voltage monitoring */
|
||||
return regmap_update_bits(data->regmap, REG_VMON_ENABLE, 0x03, 0x03);
|
||||
}
|
||||
|
||||
static int nct7802_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
struct device *dev = &client->dev;
|
||||
struct nct7802_data *data;
|
||||
struct device *hwmon_dev;
|
||||
int ret;
|
||||
|
||||
data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
|
||||
if (data == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
data->regmap = devm_regmap_init_i2c(client, &nct7802_regmap_config);
|
||||
if (IS_ERR(data->regmap))
|
||||
return PTR_ERR(data->regmap);
|
||||
|
||||
mutex_init(&data->access_lock);
|
||||
|
||||
ret = nct7802_init_chip(data);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
hwmon_dev = devm_hwmon_device_register_with_groups(dev, client->name,
|
||||
data,
|
||||
nct7802_groups);
|
||||
return PTR_ERR_OR_ZERO(hwmon_dev);
|
||||
}
|
||||
|
||||
static const unsigned short nct7802_address_list[] = {
|
||||
0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, I2C_CLIENT_END
|
||||
};
|
||||
|
||||
static const struct i2c_device_id nct7802_idtable[] = {
|
||||
{ "nct7802", 0 },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, nct7802_idtable);
|
||||
|
||||
static struct i2c_driver nct7802_driver = {
|
||||
.class = I2C_CLASS_HWMON,
|
||||
.driver = {
|
||||
.name = DRVNAME,
|
||||
},
|
||||
.detect = nct7802_detect,
|
||||
.probe = nct7802_probe,
|
||||
.id_table = nct7802_idtable,
|
||||
.address_list = nct7802_address_list,
|
||||
};
|
||||
|
||||
module_i2c_driver(nct7802_driver);
|
||||
|
||||
MODULE_AUTHOR("Guenter Roeck <linux@roeck-us.net>");
|
||||
MODULE_DESCRIPTION("NCT7802Y Hardware Monitoring Driver");
|
||||
MODULE_LICENSE("GPL v2");
|
|
@ -47,15 +47,22 @@ config SENSORS_LM25066
|
|||
be called lm25066.
|
||||
|
||||
config SENSORS_LTC2978
|
||||
tristate "Linear Technologies LTC2974, LTC2978, LTC3880, and LTC3883"
|
||||
tristate "Linear Technologies LTC2978 and compatibles"
|
||||
default n
|
||||
help
|
||||
If you say yes here you get hardware monitoring support for Linear
|
||||
Technology LTC2974, LTC2978, LTC3880, and LTC3883.
|
||||
Technology LTC2974, LTC2977, LTC2978, LTC3880, LTC3883, and LTM4676.
|
||||
|
||||
This driver can also be built as a module. If so, the module will
|
||||
be called ltc2978.
|
||||
|
||||
config SENSORS_LTC2978_REGULATOR
|
||||
boolean "Regulator support for LTC2978 and compatibles"
|
||||
depends on SENSORS_LTC2978 && REGULATOR
|
||||
help
|
||||
If you say yes here you get regulator support for Linear
|
||||
Technology LTC2974, LTC2977, LTC2978, LTC3880, LTC3883, and LTM4676.
|
||||
|
||||
config SENSORS_MAX16064
|
||||
tristate "Maxim MAX16064"
|
||||
default n
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
#include <linux/err.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/regulator/driver.h>
|
||||
#include "pmbus.h"
|
||||
|
||||
enum chips { ltc2974, ltc2977, ltc2978, ltc3880, ltc3883, ltm4676 };
|
||||
|
@ -374,6 +375,19 @@ static const struct i2c_device_id ltc2978_id[] = {
|
|||
};
|
||||
MODULE_DEVICE_TABLE(i2c, ltc2978_id);
|
||||
|
||||
#if IS_ENABLED(CONFIG_SENSORS_LTC2978_REGULATOR)
|
||||
static const struct regulator_desc ltc2978_reg_desc[] = {
|
||||
PMBUS_REGULATOR("vout", 0),
|
||||
PMBUS_REGULATOR("vout", 1),
|
||||
PMBUS_REGULATOR("vout", 2),
|
||||
PMBUS_REGULATOR("vout", 3),
|
||||
PMBUS_REGULATOR("vout", 4),
|
||||
PMBUS_REGULATOR("vout", 5),
|
||||
PMBUS_REGULATOR("vout", 6),
|
||||
PMBUS_REGULATOR("vout", 7),
|
||||
};
|
||||
#endif /* CONFIG_SENSORS_LTC2978_REGULATOR */
|
||||
|
||||
static int ltc2978_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
|
@ -487,13 +501,36 @@ static int ltc2978_probe(struct i2c_client *client,
|
|||
default:
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
#if IS_ENABLED(CONFIG_SENSORS_LTC2978_REGULATOR)
|
||||
info->num_regulators = info->pages;
|
||||
info->reg_desc = ltc2978_reg_desc;
|
||||
if (info->num_regulators > ARRAY_SIZE(ltc2978_reg_desc)) {
|
||||
dev_err(&client->dev, "num_regulators too large!");
|
||||
info->num_regulators = ARRAY_SIZE(ltc2978_reg_desc);
|
||||
}
|
||||
#endif
|
||||
|
||||
return pmbus_do_probe(client, id, info);
|
||||
}
|
||||
|
||||
/* This is the driver that will be inserted */
|
||||
#ifdef CONFIG_OF
|
||||
static const struct of_device_id ltc2978_of_match[] = {
|
||||
{ .compatible = "lltc,ltc2974" },
|
||||
{ .compatible = "lltc,ltc2977" },
|
||||
{ .compatible = "lltc,ltc2978" },
|
||||
{ .compatible = "lltc,ltc3880" },
|
||||
{ .compatible = "lltc,ltc3883" },
|
||||
{ .compatible = "lltc,ltm4676" },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, ltc2978_of_match);
|
||||
#endif
|
||||
|
||||
static struct i2c_driver ltc2978_driver = {
|
||||
.driver = {
|
||||
.name = "ltc2978",
|
||||
.of_match_table = of_match_ptr(ltc2978_of_match),
|
||||
},
|
||||
.probe = ltc2978_probe,
|
||||
.remove = pmbus_do_remove,
|
||||
|
|
|
@ -19,6 +19,8 @@
|
|||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include <linux/regulator/driver.h>
|
||||
|
||||
#ifndef PMBUS_H
|
||||
#define PMBUS_H
|
||||
|
||||
|
@ -185,6 +187,11 @@
|
|||
#define PMBUS_VIRT_VMON_OV_FAULT_LIMIT (PMBUS_VIRT_BASE + 34)
|
||||
#define PMBUS_VIRT_STATUS_VMON (PMBUS_VIRT_BASE + 35)
|
||||
|
||||
/*
|
||||
* OPERATION
|
||||
*/
|
||||
#define PB_OPERATION_CONTROL_ON (1<<7)
|
||||
|
||||
/*
|
||||
* CAPABILITY
|
||||
*/
|
||||
|
@ -365,8 +372,27 @@ struct pmbus_driver_info {
|
|||
*/
|
||||
int (*identify)(struct i2c_client *client,
|
||||
struct pmbus_driver_info *info);
|
||||
|
||||
/* Regulator functionality, if supported by this chip driver. */
|
||||
int num_regulators;
|
||||
const struct regulator_desc *reg_desc;
|
||||
};
|
||||
|
||||
/* Regulator ops */
|
||||
|
||||
extern struct regulator_ops pmbus_regulator_ops;
|
||||
|
||||
/* Macro for filling in array of struct regulator_desc */
|
||||
#define PMBUS_REGULATOR(_name, _id) \
|
||||
[_id] = { \
|
||||
.name = (_name # _id), \
|
||||
.id = (_id), \
|
||||
.of_match = of_match_ptr(_name # _id), \
|
||||
.regulators_node = of_match_ptr("regulators"), \
|
||||
.ops = &pmbus_regulator_ops, \
|
||||
.owner = THIS_MODULE, \
|
||||
}
|
||||
|
||||
/* Function declarations */
|
||||
|
||||
void pmbus_clear_cache(struct i2c_client *client);
|
||||
|
@ -375,6 +401,10 @@ int pmbus_read_word_data(struct i2c_client *client, u8 page, u8 reg);
|
|||
int pmbus_write_word_data(struct i2c_client *client, u8 page, u8 reg, u16 word);
|
||||
int pmbus_read_byte_data(struct i2c_client *client, int page, u8 reg);
|
||||
int pmbus_write_byte(struct i2c_client *client, int page, u8 value);
|
||||
int pmbus_write_byte_data(struct i2c_client *client, int page, u8 reg,
|
||||
u8 value);
|
||||
int pmbus_update_byte_data(struct i2c_client *client, int page, u8 reg,
|
||||
u8 mask, u8 value);
|
||||
void pmbus_clear_faults(struct i2c_client *client);
|
||||
bool pmbus_check_byte_register(struct i2c_client *client, int page, int reg);
|
||||
bool pmbus_check_word_register(struct i2c_client *client, int page, int reg);
|
||||
|
|
|
@ -29,6 +29,8 @@
|
|||
#include <linux/hwmon-sysfs.h>
|
||||
#include <linux/jiffies.h>
|
||||
#include <linux/i2c/pmbus.h>
|
||||
#include <linux/regulator/driver.h>
|
||||
#include <linux/regulator/machine.h>
|
||||
#include "pmbus.h"
|
||||
|
||||
/*
|
||||
|
@ -253,6 +255,37 @@ int pmbus_read_byte_data(struct i2c_client *client, int page, u8 reg)
|
|||
}
|
||||
EXPORT_SYMBOL_GPL(pmbus_read_byte_data);
|
||||
|
||||
int pmbus_write_byte_data(struct i2c_client *client, int page, u8 reg, u8 value)
|
||||
{
|
||||
int rv;
|
||||
|
||||
rv = pmbus_set_page(client, page);
|
||||
if (rv < 0)
|
||||
return rv;
|
||||
|
||||
return i2c_smbus_write_byte_data(client, reg, value);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(pmbus_write_byte_data);
|
||||
|
||||
int pmbus_update_byte_data(struct i2c_client *client, int page, u8 reg,
|
||||
u8 mask, u8 value)
|
||||
{
|
||||
unsigned int tmp;
|
||||
int rv;
|
||||
|
||||
rv = pmbus_read_byte_data(client, page, reg);
|
||||
if (rv < 0)
|
||||
return rv;
|
||||
|
||||
tmp = (rv & ~mask) | (value & mask);
|
||||
|
||||
if (tmp != rv)
|
||||
rv = pmbus_write_byte_data(client, page, reg, tmp);
|
||||
|
||||
return rv;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(pmbus_update_byte_data);
|
||||
|
||||
/*
|
||||
* _pmbus_read_byte_data() is similar to pmbus_read_byte_data(), but checks if
|
||||
* a device specific mapping function exists and calls it if necessary.
|
||||
|
@ -1727,6 +1760,84 @@ static int pmbus_init_common(struct i2c_client *client, struct pmbus_data *data,
|
|||
return 0;
|
||||
}
|
||||
|
||||
#if IS_ENABLED(CONFIG_REGULATOR)
|
||||
static int pmbus_regulator_is_enabled(struct regulator_dev *rdev)
|
||||
{
|
||||
struct device *dev = rdev_get_dev(rdev);
|
||||
struct i2c_client *client = to_i2c_client(dev->parent);
|
||||
u8 page = rdev_get_id(rdev);
|
||||
int ret;
|
||||
|
||||
ret = pmbus_read_byte_data(client, page, PMBUS_OPERATION);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return !!(ret & PB_OPERATION_CONTROL_ON);
|
||||
}
|
||||
|
||||
static int _pmbus_regulator_on_off(struct regulator_dev *rdev, bool enable)
|
||||
{
|
||||
struct device *dev = rdev_get_dev(rdev);
|
||||
struct i2c_client *client = to_i2c_client(dev->parent);
|
||||
u8 page = rdev_get_id(rdev);
|
||||
|
||||
return pmbus_update_byte_data(client, page, PMBUS_OPERATION,
|
||||
PB_OPERATION_CONTROL_ON,
|
||||
enable ? PB_OPERATION_CONTROL_ON : 0);
|
||||
}
|
||||
|
||||
static int pmbus_regulator_enable(struct regulator_dev *rdev)
|
||||
{
|
||||
return _pmbus_regulator_on_off(rdev, 1);
|
||||
}
|
||||
|
||||
static int pmbus_regulator_disable(struct regulator_dev *rdev)
|
||||
{
|
||||
return _pmbus_regulator_on_off(rdev, 0);
|
||||
}
|
||||
|
||||
struct regulator_ops pmbus_regulator_ops = {
|
||||
.enable = pmbus_regulator_enable,
|
||||
.disable = pmbus_regulator_disable,
|
||||
.is_enabled = pmbus_regulator_is_enabled,
|
||||
};
|
||||
EXPORT_SYMBOL_GPL(pmbus_regulator_ops);
|
||||
|
||||
static int pmbus_regulator_register(struct pmbus_data *data)
|
||||
{
|
||||
struct device *dev = data->dev;
|
||||
const struct pmbus_driver_info *info = data->info;
|
||||
const struct pmbus_platform_data *pdata = dev_get_platdata(dev);
|
||||
struct regulator_dev *rdev;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < info->num_regulators; i++) {
|
||||
struct regulator_config config = { };
|
||||
|
||||
config.dev = dev;
|
||||
config.driver_data = data;
|
||||
|
||||
if (pdata && pdata->reg_init_data)
|
||||
config.init_data = &pdata->reg_init_data[i];
|
||||
|
||||
rdev = devm_regulator_register(dev, &info->reg_desc[i],
|
||||
&config);
|
||||
if (IS_ERR(rdev)) {
|
||||
dev_err(dev, "Failed to register %s regulator\n",
|
||||
info->reg_desc[i].name);
|
||||
return PTR_ERR(rdev);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
#else
|
||||
static int pmbus_regulator_register(struct pmbus_data *data)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
int pmbus_do_probe(struct i2c_client *client, const struct i2c_device_id *id,
|
||||
struct pmbus_driver_info *info)
|
||||
{
|
||||
|
@ -1781,8 +1892,15 @@ int pmbus_do_probe(struct i2c_client *client, const struct i2c_device_id *id,
|
|||
dev_err(dev, "Failed to register hwmon device\n");
|
||||
goto out_kfree;
|
||||
}
|
||||
|
||||
ret = pmbus_regulator_register(data);
|
||||
if (ret)
|
||||
goto out_unregister;
|
||||
|
||||
return 0;
|
||||
|
||||
out_unregister:
|
||||
hwmon_device_unregister(data->hwmon_dev);
|
||||
out_kfree:
|
||||
kfree(data->group.attrs);
|
||||
return ret;
|
||||
|
|
|
@ -44,9 +44,10 @@
|
|||
#include <linux/sysfs.h>
|
||||
|
||||
/* Addresses to scan */
|
||||
static const unsigned short normal_i2c[] = { 0x4c, 0x4d, 0x4e, I2C_CLIENT_END };
|
||||
static const unsigned short normal_i2c[] = { 0x37, 0x48, 0x49, 0x4a, 0x4c, 0x4d,
|
||||
0x4e, 0x4f, I2C_CLIENT_END };
|
||||
|
||||
enum chips { tmp401, tmp411, tmp431, tmp432 };
|
||||
enum chips { tmp401, tmp411, tmp431, tmp432, tmp435 };
|
||||
|
||||
/*
|
||||
* The TMP401 registers, note some registers have different addresses for
|
||||
|
@ -136,6 +137,7 @@ static const u8 TMP432_STATUS_REG[] = {
|
|||
#define TMP411C_DEVICE_ID 0x10
|
||||
#define TMP431_DEVICE_ID 0x31
|
||||
#define TMP432_DEVICE_ID 0x32
|
||||
#define TMP435_DEVICE_ID 0x35
|
||||
|
||||
/*
|
||||
* Driver data (common to all clients)
|
||||
|
@ -146,6 +148,7 @@ static const struct i2c_device_id tmp401_id[] = {
|
|||
{ "tmp411", tmp411 },
|
||||
{ "tmp431", tmp431 },
|
||||
{ "tmp432", tmp432 },
|
||||
{ "tmp435", tmp435 },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, tmp401_id);
|
||||
|
@ -613,10 +616,10 @@ static const struct attribute_group tmp432_group = {
|
|||
* Begin non sysfs callback code (aka Real code)
|
||||
*/
|
||||
|
||||
static void tmp401_init_client(struct tmp401_data *data,
|
||||
struct i2c_client *client)
|
||||
static int tmp401_init_client(struct tmp401_data *data,
|
||||
struct i2c_client *client)
|
||||
{
|
||||
int config, config_orig;
|
||||
int config, config_orig, status = 0;
|
||||
|
||||
/* Set the conversion rate to 2 Hz */
|
||||
i2c_smbus_write_byte_data(client, TMP401_CONVERSION_RATE_WRITE, 5);
|
||||
|
@ -624,16 +627,18 @@ static void tmp401_init_client(struct tmp401_data *data,
|
|||
|
||||
/* Start conversions (disable shutdown if necessary) */
|
||||
config = i2c_smbus_read_byte_data(client, TMP401_CONFIG_READ);
|
||||
if (config < 0) {
|
||||
dev_warn(&client->dev, "Initialization failed!\n");
|
||||
return;
|
||||
}
|
||||
if (config < 0)
|
||||
return config;
|
||||
|
||||
config_orig = config;
|
||||
config &= ~TMP401_CONFIG_SHUTDOWN;
|
||||
|
||||
if (config != config_orig)
|
||||
i2c_smbus_write_byte_data(client, TMP401_CONFIG_WRITE, config);
|
||||
status = i2c_smbus_write_byte_data(client,
|
||||
TMP401_CONFIG_WRITE,
|
||||
config);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
static int tmp401_detect(struct i2c_client *client,
|
||||
|
@ -675,15 +680,18 @@ static int tmp401_detect(struct i2c_client *client,
|
|||
kind = tmp411;
|
||||
break;
|
||||
case TMP431_DEVICE_ID:
|
||||
if (client->addr == 0x4e)
|
||||
if (client->addr != 0x4c && client->addr != 0x4d)
|
||||
return -ENODEV;
|
||||
kind = tmp431;
|
||||
break;
|
||||
case TMP432_DEVICE_ID:
|
||||
if (client->addr == 0x4e)
|
||||
if (client->addr != 0x4c && client->addr != 0x4d)
|
||||
return -ENODEV;
|
||||
kind = tmp432;
|
||||
break;
|
||||
case TMP435_DEVICE_ID:
|
||||
kind = tmp435;
|
||||
break;
|
||||
default:
|
||||
return -ENODEV;
|
||||
}
|
||||
|
@ -705,11 +713,13 @@ static int tmp401_detect(struct i2c_client *client,
|
|||
static int tmp401_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
const char *names[] = { "TMP401", "TMP411", "TMP431", "TMP432" };
|
||||
static const char * const names[] = {
|
||||
"TMP401", "TMP411", "TMP431", "TMP432", "TMP435"
|
||||
};
|
||||
struct device *dev = &client->dev;
|
||||
struct device *hwmon_dev;
|
||||
struct tmp401_data *data;
|
||||
int groups = 0;
|
||||
int groups = 0, status;
|
||||
|
||||
data = devm_kzalloc(dev, sizeof(struct tmp401_data), GFP_KERNEL);
|
||||
if (!data)
|
||||
|
@ -720,7 +730,9 @@ static int tmp401_probe(struct i2c_client *client,
|
|||
data->kind = id->driver_data;
|
||||
|
||||
/* Initialize the TMP401 chip */
|
||||
tmp401_init_client(data, client);
|
||||
status = tmp401_init_client(data, client);
|
||||
if (status < 0)
|
||||
return status;
|
||||
|
||||
/* Register sysfs hooks */
|
||||
data->groups[groups++] = &tmp401_group;
|
||||
|
|
|
@ -40,6 +40,10 @@
|
|||
|
||||
struct pmbus_platform_data {
|
||||
u32 flags; /* Device specific flags */
|
||||
|
||||
/* regulator support */
|
||||
int num_regulators;
|
||||
struct regulator_init_data *reg_init_data;
|
||||
};
|
||||
|
||||
#endif /* _PMBUS_H_ */
|
||||
|
|
Загрузка…
Ссылка в новой задаче