hwmon updates for v4.4
New driver for MAX31790, added support for TMP75C, as well as cleanups and minor improvements in various drivers. -----BEGIN PGP SIGNATURE----- Version: GnuPG v1 iQIcBAABAgAGBQJWNrTUAAoJEMsfJm/On5mBq8MP/3MHJNGdHIB/WlHlVflaSkG9 qouMLDasxRlVXRsrO4lXTXHuq+PlMATcfLxmJcIwy6CcvYxU0PczpfbVvtU2wmvZ TXHNDDfcR9fQ25ewpvVFN20BD22bJaJLkqi9gUIt8Rs/del4aQ4gpNUR3I7pHjTJ /MpRKgcEq9PMy1AZEjNgQRW7bnHC4xedy62uLjYoOVZ55AJKkdEoA1K/XgiHVdvh mpZdG4yVWA4Ukb1RWWBmuwIIn7iVzFHI58ijFO03EmDAvpF5se7RrLS4RKjANOv3 x3FgoWphEtnk5XqKckNPFoBHXlWrvknyLx43LX0uTRdWIe9SHzt1PQplB7ZV6iak 8x89BAd8dRZF7lm61+n3e78ekaSjPaFTQCIQLO4DTi0YHAOY8a/hZNCTAAvZNfTY epOH6gXGsAjDXmeiBPOYtYbyS4AtdberB7VAFLID5n2HqP0ukdiavIKr2OvbUgI+ X0H4/iDbv79TZ7GhMl6dSCJ6rnWAp5D8TxpYwncShFDwD29VyWsgqUAg+OD5Fk5m YXpisVWP65KzGyUB/JLw4cKSVrWuz7aEsTr7sJjLVHcPV+pwgunl4FWwibzog+is E20I67SFI2u6CRcKDWtVDPXkd90S2NKdESl4hvz/CquEScH9DmmJ4QPAqq5Gqkeg Nj0Xbo9ds36+2LURz4gW =Fa0c -----END PGP SIGNATURE----- Merge tag 'hwmon-for-linus-v4.4' of git://git.kernel.org/pub/scm/linux/kernel/git/groeck/linux-staging Pull hwmon updates from Guenter Roeck: "New driver for MAX31790, added support for TMP75C, as well as cleanups and minor improvements in various drivers" * tag 'hwmon-for-linus-v4.4' of git://git.kernel.org/pub/scm/linux/kernel/git/groeck/linux-staging: hwmon: (fam15h_power) Add max compute unit accumulated power hwmon: (fam15h_power) Enable power1_input on AMD Carrizo hwmon: (fam15h_power) Refactor attributes for dynamically added hwmon: (ina2xx) remove no longer used variable 'kind' hwmon: (nct6775) Introduce separate temperature labels for NCT6792 and NCT6793 hwmon: (nct6775) NCT6791D and NCT6792D have an additional temperature source hwmon: (ina2xx) give precedence to DT over checking for platform data. hwmon: (ina2xx) convert driver to using regmap hwmon: (coretemp) Increase limit of maximum core ID from 32 to 128. hwmon: (lm75) Add support for TMP75C hwmon: (ibmpowernv) Add OF compatibility table entry hwmon: (abx500) drop the use of IRQF_NO_SUSPEND hwmon: (max31790) Fix dereference of ERR_PTR hwmon: Driver for Maxim MAX31790
This commit is contained in:
commit
66b0199678
@ -42,8 +42,8 @@ Supported chips:
|
||||
Addresses scanned: none
|
||||
Datasheet: Publicly available at the ST website
|
||||
http://www.st.com/internet/analog/product/121769.jsp
|
||||
* Texas Instruments TMP100, TMP101, TMP105, TMP112, TMP75, TMP175, TMP275
|
||||
Prefixes: 'tmp100', 'tmp101', 'tmp105', 'tmp112', 'tmp175', 'tmp75', 'tmp275'
|
||||
* Texas Instruments TMP100, TMP101, TMP105, TMP112, TMP75, TMP75C, TMP175, TMP275
|
||||
Prefixes: 'tmp100', 'tmp101', 'tmp105', 'tmp112', 'tmp175', 'tmp75', 'tmp75c', 'tmp275'
|
||||
Addresses scanned: none
|
||||
Datasheet: Publicly available at the Texas Instruments website
|
||||
http://www.ti.com/product/tmp100
|
||||
@ -51,6 +51,7 @@ Supported chips:
|
||||
http://www.ti.com/product/tmp105
|
||||
http://www.ti.com/product/tmp112
|
||||
http://www.ti.com/product/tmp75
|
||||
http://www.ti.com/product/tmp75c
|
||||
http://www.ti.com/product/tmp175
|
||||
http://www.ti.com/product/tmp275
|
||||
* NXP LM75B
|
||||
|
37
Documentation/hwmon/max31790
Normal file
37
Documentation/hwmon/max31790
Normal file
@ -0,0 +1,37 @@
|
||||
Kernel driver max31790
|
||||
======================
|
||||
|
||||
Supported chips:
|
||||
* Maxim MAX31790
|
||||
Prefix: 'max31790'
|
||||
Addresses scanned: -
|
||||
Datasheet: http://pdfserv.maximintegrated.com/en/ds/MAX31790.pdf
|
||||
|
||||
Author: Il Han <corone.il.han@gmail.com>
|
||||
|
||||
|
||||
Description
|
||||
-----------
|
||||
|
||||
This driver implements support for the Maxim MAX31790 chip.
|
||||
|
||||
The MAX31790 controls the speeds of up to six fans using six independent
|
||||
PWM outputs. The desired fan speeds (or PWM duty cycles) are written
|
||||
through the I2C interface. The outputs drive "4-wire" fans directly,
|
||||
or can be used to modulate the fan's power terminals using an external
|
||||
pass transistor.
|
||||
|
||||
Tachometer inputs monitor fan tachometer logic outputs for precise (+/-1%)
|
||||
monitoring and control of fan RPM as well as detection of fan failure.
|
||||
Six pins are dedicated tachometer inputs. Any of the six PWM outputs can
|
||||
also be configured to serve as tachometer inputs.
|
||||
|
||||
|
||||
Sysfs entries
|
||||
-------------
|
||||
|
||||
fan[1-12]_input RO fan tachometer speed in RPM
|
||||
fan[1-12]_fault RO fan experienced fault
|
||||
fan[1-6]_target RW desired fan speed in RPM
|
||||
pwm[1-6]_enable RW regulator mode, 0=disabled, 1=manual mode, 2=rpm mode
|
||||
pwm[1-6] RW fan target duty cycle (0-255)
|
@ -840,6 +840,16 @@ config SENSORS_MAX6697
|
||||
This driver can also be built as a module. If so, the module
|
||||
will be called max6697.
|
||||
|
||||
config SENSORS_MAX31790
|
||||
tristate "Maxim MAX31790 sensor chip"
|
||||
depends on I2C
|
||||
help
|
||||
If you say yes here you get support for 6-Channel PWM-Output
|
||||
Fan RPM Controller.
|
||||
|
||||
This driver can also be built as a module. If so, the module
|
||||
will be called max31790.
|
||||
|
||||
config SENSORS_HTU21
|
||||
tristate "Measurement Specialties HTU21D humidity/temperature sensors"
|
||||
depends on I2C
|
||||
|
@ -115,6 +115,7 @@ obj-$(CONFIG_SENSORS_MAX6639) += max6639.o
|
||||
obj-$(CONFIG_SENSORS_MAX6642) += max6642.o
|
||||
obj-$(CONFIG_SENSORS_MAX6650) += max6650.o
|
||||
obj-$(CONFIG_SENSORS_MAX6697) += max6697.o
|
||||
obj-$(CONFIG_SENSORS_MAX31790) += max31790.o
|
||||
obj-$(CONFIG_SENSORS_MC13783_ADC)+= mc13783-adc.o
|
||||
obj-$(CONFIG_SENSORS_MCP3021) += mcp3021.o
|
||||
obj-$(CONFIG_SENSORS_MENF21BMC_HWMON) += menf21bmc_hwmon.o
|
||||
|
@ -377,7 +377,7 @@ static int setup_irqs(struct platform_device *pdev)
|
||||
}
|
||||
|
||||
ret = devm_request_threaded_irq(&pdev->dev, irq, NULL,
|
||||
abx500_temp_irq_handler, IRQF_NO_SUSPEND, "abx500-temp", pdev);
|
||||
abx500_temp_irq_handler, 0, "abx500-temp", pdev);
|
||||
if (ret < 0)
|
||||
dev_err(&pdev->dev, "Request threaded irq failed (%d)\n", ret);
|
||||
|
||||
|
@ -52,7 +52,7 @@ module_param_named(tjmax, force_tjmax, int, 0444);
|
||||
MODULE_PARM_DESC(tjmax, "TjMax value in degrees Celsius");
|
||||
|
||||
#define BASE_SYSFS_ATTR_NO 2 /* Sysfs Base attr no for coretemp */
|
||||
#define NUM_REAL_CORES 32 /* Number of Real cores per cpu */
|
||||
#define NUM_REAL_CORES 128 /* Number of Real cores per cpu */
|
||||
#define CORETEMP_NAME_LENGTH 19 /* String Length of attrs */
|
||||
#define MAX_CORE_ATTRS 4 /* Maximum no of basic attrs */
|
||||
#define TOTAL_ATTRS (MAX_CORE_ATTRS + 1)
|
||||
|
@ -26,6 +26,7 @@
|
||||
#include <linux/pci.h>
|
||||
#include <linux/bitops.h>
|
||||
#include <asm/processor.h>
|
||||
#include <asm/msr.h>
|
||||
|
||||
MODULE_DESCRIPTION("AMD Family 15h CPU processor power monitor");
|
||||
MODULE_AUTHOR("Andreas Herrmann <herrmann.der.user@googlemail.com>");
|
||||
@ -41,12 +42,21 @@ MODULE_LICENSE("GPL");
|
||||
#define REG_TDP_RUNNING_AVERAGE 0xe0
|
||||
#define REG_TDP_LIMIT3 0xe8
|
||||
|
||||
#define FAM15H_MIN_NUM_ATTRS 2
|
||||
#define FAM15H_NUM_GROUPS 2
|
||||
|
||||
#define MSR_F15H_CU_MAX_PWR_ACCUMULATOR 0xc001007b
|
||||
|
||||
struct fam15h_power_data {
|
||||
struct pci_dev *pdev;
|
||||
unsigned int tdp_to_watts;
|
||||
unsigned int base_tdp;
|
||||
unsigned int processor_pwr_watts;
|
||||
unsigned int cpu_pwr_sample_ratio;
|
||||
const struct attribute_group *groups[FAM15H_NUM_GROUPS];
|
||||
struct attribute_group group;
|
||||
/* maximum accumulated power of a compute unit */
|
||||
u64 max_cu_acc_power;
|
||||
};
|
||||
|
||||
static ssize_t show_power(struct device *dev,
|
||||
@ -105,30 +115,37 @@ static ssize_t show_power_crit(struct device *dev,
|
||||
}
|
||||
static DEVICE_ATTR(power1_crit, S_IRUGO, show_power_crit, NULL);
|
||||
|
||||
static umode_t fam15h_power_is_visible(struct kobject *kobj,
|
||||
struct attribute *attr,
|
||||
int index)
|
||||
static int fam15h_power_init_attrs(struct pci_dev *pdev,
|
||||
struct fam15h_power_data *data)
|
||||
{
|
||||
/* power1_input is only reported for Fam15h, Models 00h-0fh */
|
||||
if (attr == &dev_attr_power1_input.attr &&
|
||||
(boot_cpu_data.x86 != 0x15 || boot_cpu_data.x86_model > 0xf))
|
||||
return 0;
|
||||
int n = FAM15H_MIN_NUM_ATTRS;
|
||||
struct attribute **fam15h_power_attrs;
|
||||
struct cpuinfo_x86 *c = &boot_cpu_data;
|
||||
|
||||
return attr->mode;
|
||||
if (c->x86 == 0x15 &&
|
||||
(c->x86_model <= 0xf ||
|
||||
(c->x86_model >= 0x60 && c->x86_model <= 0x6f)))
|
||||
n += 1;
|
||||
|
||||
fam15h_power_attrs = devm_kcalloc(&pdev->dev, n,
|
||||
sizeof(*fam15h_power_attrs),
|
||||
GFP_KERNEL);
|
||||
|
||||
if (!fam15h_power_attrs)
|
||||
return -ENOMEM;
|
||||
|
||||
n = 0;
|
||||
fam15h_power_attrs[n++] = &dev_attr_power1_crit.attr;
|
||||
if (c->x86 == 0x15 &&
|
||||
(c->x86_model <= 0xf ||
|
||||
(c->x86_model >= 0x60 && c->x86_model <= 0x6f)))
|
||||
fam15h_power_attrs[n++] = &dev_attr_power1_input.attr;
|
||||
|
||||
data->group.attrs = fam15h_power_attrs;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct attribute *fam15h_power_attrs[] = {
|
||||
&dev_attr_power1_input.attr,
|
||||
&dev_attr_power1_crit.attr,
|
||||
NULL
|
||||
};
|
||||
|
||||
static const struct attribute_group fam15h_power_group = {
|
||||
.attrs = fam15h_power_attrs,
|
||||
.is_visible = fam15h_power_is_visible,
|
||||
};
|
||||
__ATTRIBUTE_GROUPS(fam15h_power);
|
||||
|
||||
static bool should_load_on_this_node(struct pci_dev *f4)
|
||||
{
|
||||
u32 val;
|
||||
@ -186,11 +203,12 @@ static int fam15h_power_resume(struct pci_dev *pdev)
|
||||
#define fam15h_power_resume NULL
|
||||
#endif
|
||||
|
||||
static void fam15h_power_init_data(struct pci_dev *f4,
|
||||
struct fam15h_power_data *data)
|
||||
static int fam15h_power_init_data(struct pci_dev *f4,
|
||||
struct fam15h_power_data *data)
|
||||
{
|
||||
u32 val, eax, ebx, ecx, edx;
|
||||
u64 tmp;
|
||||
int ret;
|
||||
|
||||
pci_read_config_dword(f4, REG_PROCESSOR_TDP, &val);
|
||||
data->base_tdp = val >> 16;
|
||||
@ -211,11 +229,15 @@ static void fam15h_power_init_data(struct pci_dev *f4,
|
||||
/* convert to microWatt */
|
||||
data->processor_pwr_watts = (tmp * 15625) >> 10;
|
||||
|
||||
ret = fam15h_power_init_attrs(f4, data);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
cpuid(0x80000007, &eax, &ebx, &ecx, &edx);
|
||||
|
||||
/* CPUID Fn8000_0007:EDX[12] indicates to support accumulated power */
|
||||
if (!(edx & BIT(12)))
|
||||
return;
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* determine the ratio of the compute unit power accumulator
|
||||
@ -223,14 +245,24 @@ static void fam15h_power_init_data(struct pci_dev *f4,
|
||||
* Fn8000_0007:ECX
|
||||
*/
|
||||
data->cpu_pwr_sample_ratio = ecx;
|
||||
|
||||
if (rdmsrl_safe(MSR_F15H_CU_MAX_PWR_ACCUMULATOR, &tmp)) {
|
||||
pr_err("Failed to read max compute unit power accumulator MSR\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
data->max_cu_acc_power = tmp;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int fam15h_power_probe(struct pci_dev *pdev,
|
||||
const struct pci_device_id *id)
|
||||
const struct pci_device_id *id)
|
||||
{
|
||||
struct fam15h_power_data *data;
|
||||
struct device *dev = &pdev->dev;
|
||||
struct device *hwmon_dev;
|
||||
int ret;
|
||||
|
||||
/*
|
||||
* though we ignore every other northbridge, we still have to
|
||||
@ -246,12 +278,17 @@ static int fam15h_power_probe(struct pci_dev *pdev,
|
||||
if (!data)
|
||||
return -ENOMEM;
|
||||
|
||||
fam15h_power_init_data(pdev, data);
|
||||
ret = fam15h_power_init_data(pdev, data);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
data->pdev = pdev;
|
||||
|
||||
data->groups[0] = &data->group;
|
||||
|
||||
hwmon_dev = devm_hwmon_device_register_with_groups(dev, "fam15h_power",
|
||||
data,
|
||||
fam15h_power_groups);
|
||||
&data->groups[0]);
|
||||
return PTR_ERR_OR_ZERO(hwmon_dev);
|
||||
}
|
||||
|
||||
|
@ -474,11 +474,18 @@ static const struct platform_device_id opal_sensor_driver_ids[] = {
|
||||
};
|
||||
MODULE_DEVICE_TABLE(platform, opal_sensor_driver_ids);
|
||||
|
||||
static const struct of_device_id opal_sensor_match[] = {
|
||||
{ .compatible = "ibm,opal-sensor" },
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, opal_sensor_match);
|
||||
|
||||
static struct platform_driver ibmpowernv_driver = {
|
||||
.probe = ibmpowernv_probe,
|
||||
.id_table = opal_sensor_driver_ids,
|
||||
.driver = {
|
||||
.name = DRVNAME,
|
||||
.of_match_table = opal_sensor_match,
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -37,6 +37,7 @@
|
||||
#include <linux/of.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/util_macros.h>
|
||||
#include <linux/regmap.h>
|
||||
|
||||
#include <linux/platform_data/ina2xx.h>
|
||||
|
||||
@ -84,6 +85,11 @@
|
||||
*/
|
||||
#define INA226_TOTAL_CONV_TIME_DEFAULT 2200
|
||||
|
||||
static struct regmap_config ina2xx_regmap_config = {
|
||||
.reg_bits = 8,
|
||||
.val_bits = 16,
|
||||
};
|
||||
|
||||
enum ina2xx_ids { ina219, ina226 };
|
||||
|
||||
struct ina2xx_config {
|
||||
@ -97,20 +103,13 @@ struct ina2xx_config {
|
||||
};
|
||||
|
||||
struct ina2xx_data {
|
||||
struct i2c_client *client;
|
||||
const struct ina2xx_config *config;
|
||||
|
||||
long rshunt;
|
||||
u16 curr_config;
|
||||
struct mutex config_lock;
|
||||
struct regmap *regmap;
|
||||
|
||||
struct mutex update_lock;
|
||||
bool valid;
|
||||
unsigned long last_updated;
|
||||
int update_interval; /* in jiffies */
|
||||
|
||||
int kind;
|
||||
const struct attribute_group *groups[INA2XX_MAX_ATTRIBUTE_GROUPS];
|
||||
u16 regs[INA2XX_MAX_REGISTERS];
|
||||
};
|
||||
|
||||
static const struct ina2xx_config ina2xx_config[] = {
|
||||
@ -153,7 +152,11 @@ static int ina226_reg_to_interval(u16 config)
|
||||
return DIV_ROUND_CLOSEST(avg * INA226_TOTAL_CONV_TIME_DEFAULT, 1000);
|
||||
}
|
||||
|
||||
static u16 ina226_interval_to_reg(int interval, u16 config)
|
||||
/*
|
||||
* Return the new, shifted AVG field value of CONFIG register,
|
||||
* to use with regmap_update_bits
|
||||
*/
|
||||
static u16 ina226_interval_to_reg(int interval)
|
||||
{
|
||||
int avg, avg_bits;
|
||||
|
||||
@ -162,15 +165,7 @@ static u16 ina226_interval_to_reg(int interval, u16 config)
|
||||
avg_bits = find_closest(avg, ina226_avg_tab,
|
||||
ARRAY_SIZE(ina226_avg_tab));
|
||||
|
||||
return (config & ~INA226_AVG_RD_MASK) | INA226_SHIFT_AVG(avg_bits);
|
||||
}
|
||||
|
||||
static void ina226_set_update_interval(struct ina2xx_data *data)
|
||||
{
|
||||
int ms;
|
||||
|
||||
ms = ina226_reg_to_interval(data->curr_config);
|
||||
data->update_interval = msecs_to_jiffies(ms);
|
||||
return INA226_SHIFT_AVG(avg_bits);
|
||||
}
|
||||
|
||||
static int ina2xx_calibrate(struct ina2xx_data *data)
|
||||
@ -178,8 +173,7 @@ static int ina2xx_calibrate(struct ina2xx_data *data)
|
||||
u16 val = DIV_ROUND_CLOSEST(data->config->calibration_factor,
|
||||
data->rshunt);
|
||||
|
||||
return i2c_smbus_write_word_swapped(data->client,
|
||||
INA2XX_CALIBRATION, val);
|
||||
return regmap_write(data->regmap, INA2XX_CALIBRATION, val);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -187,12 +181,8 @@ static int ina2xx_calibrate(struct ina2xx_data *data)
|
||||
*/
|
||||
static int ina2xx_init(struct ina2xx_data *data)
|
||||
{
|
||||
struct i2c_client *client = data->client;
|
||||
int ret;
|
||||
|
||||
/* device configuration */
|
||||
ret = i2c_smbus_write_word_swapped(client, INA2XX_CONFIG,
|
||||
data->curr_config);
|
||||
int ret = regmap_write(data->regmap, INA2XX_CONFIG,
|
||||
data->config->config_default);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
@ -203,47 +193,52 @@ static int ina2xx_init(struct ina2xx_data *data)
|
||||
return ina2xx_calibrate(data);
|
||||
}
|
||||
|
||||
static int ina2xx_do_update(struct device *dev)
|
||||
static int ina2xx_read_reg(struct device *dev, int reg, unsigned int *regval)
|
||||
{
|
||||
struct ina2xx_data *data = dev_get_drvdata(dev);
|
||||
struct i2c_client *client = data->client;
|
||||
int i, rv, retry;
|
||||
int ret, retry;
|
||||
|
||||
dev_dbg(&client->dev, "Starting ina2xx update\n");
|
||||
dev_dbg(dev, "Starting register %d read\n", reg);
|
||||
|
||||
for (retry = 5; retry; retry--) {
|
||||
/* Read all registers */
|
||||
for (i = 0; i < data->config->registers; i++) {
|
||||
rv = i2c_smbus_read_word_swapped(client, i);
|
||||
if (rv < 0)
|
||||
return rv;
|
||||
data->regs[i] = rv;
|
||||
}
|
||||
|
||||
ret = regmap_read(data->regmap, reg, regval);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
dev_dbg(dev, "read %d, val = 0x%04x\n", reg, *regval);
|
||||
|
||||
/*
|
||||
* If the current value in the calibration register is 0, the
|
||||
* power and current registers will also remain at 0. In case
|
||||
* the chip has been reset let's check the calibration
|
||||
* register and reinitialize if needed.
|
||||
* We do that extra read of the calibration register if there
|
||||
* is some hint of a chip reset.
|
||||
*/
|
||||
if (data->regs[INA2XX_CALIBRATION] == 0) {
|
||||
dev_warn(dev, "chip not calibrated, reinitializing\n");
|
||||
if (*regval == 0) {
|
||||
unsigned int cal;
|
||||
|
||||
rv = ina2xx_init(data);
|
||||
if (rv < 0)
|
||||
return rv;
|
||||
ret = regmap_read(data->regmap, INA2XX_CALIBRATION,
|
||||
&cal);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
/*
|
||||
* Let's make sure the power and current registers
|
||||
* have been updated before trying again.
|
||||
*/
|
||||
msleep(INA2XX_MAX_DELAY);
|
||||
continue;
|
||||
if (cal == 0) {
|
||||
dev_warn(dev, "chip not calibrated, reinitializing\n");
|
||||
|
||||
ret = ina2xx_init(data);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
/*
|
||||
* Let's make sure the power and current
|
||||
* registers have been updated before trying
|
||||
* again.
|
||||
*/
|
||||
msleep(INA2XX_MAX_DELAY);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
data->last_updated = jiffies;
|
||||
data->valid = 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -256,51 +251,31 @@ static int ina2xx_do_update(struct device *dev)
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
static struct ina2xx_data *ina2xx_update_device(struct device *dev)
|
||||
{
|
||||
struct ina2xx_data *data = dev_get_drvdata(dev);
|
||||
struct ina2xx_data *ret = data;
|
||||
unsigned long after;
|
||||
int rv;
|
||||
|
||||
mutex_lock(&data->update_lock);
|
||||
|
||||
after = data->last_updated + data->update_interval;
|
||||
if (time_after(jiffies, after) || !data->valid) {
|
||||
rv = ina2xx_do_update(dev);
|
||||
if (rv < 0)
|
||||
ret = ERR_PTR(rv);
|
||||
}
|
||||
|
||||
mutex_unlock(&data->update_lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ina2xx_get_value(struct ina2xx_data *data, u8 reg)
|
||||
static int ina2xx_get_value(struct ina2xx_data *data, u8 reg,
|
||||
unsigned int regval)
|
||||
{
|
||||
int val;
|
||||
|
||||
switch (reg) {
|
||||
case INA2XX_SHUNT_VOLTAGE:
|
||||
/* signed register */
|
||||
val = DIV_ROUND_CLOSEST((s16)data->regs[reg],
|
||||
data->config->shunt_div);
|
||||
val = DIV_ROUND_CLOSEST((s16)regval, data->config->shunt_div);
|
||||
break;
|
||||
case INA2XX_BUS_VOLTAGE:
|
||||
val = (data->regs[reg] >> data->config->bus_voltage_shift)
|
||||
val = (regval >> data->config->bus_voltage_shift)
|
||||
* data->config->bus_voltage_lsb;
|
||||
val = DIV_ROUND_CLOSEST(val, 1000);
|
||||
break;
|
||||
case INA2XX_POWER:
|
||||
val = data->regs[reg] * data->config->power_lsb;
|
||||
val = regval * data->config->power_lsb;
|
||||
break;
|
||||
case INA2XX_CURRENT:
|
||||
/* signed register, LSB=1mA (selected), in mA */
|
||||
val = (s16)data->regs[reg];
|
||||
val = (s16)regval;
|
||||
break;
|
||||
case INA2XX_CALIBRATION:
|
||||
val = DIV_ROUND_CLOSEST(data->config->calibration_factor,
|
||||
data->regs[reg]);
|
||||
regval);
|
||||
break;
|
||||
default:
|
||||
/* programmer goofed */
|
||||
@ -316,25 +291,25 @@ static ssize_t ina2xx_show_value(struct device *dev,
|
||||
struct device_attribute *da, char *buf)
|
||||
{
|
||||
struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
|
||||
struct ina2xx_data *data = ina2xx_update_device(dev);
|
||||
struct ina2xx_data *data = dev_get_drvdata(dev);
|
||||
unsigned int regval;
|
||||
|
||||
if (IS_ERR(data))
|
||||
return PTR_ERR(data);
|
||||
int err = ina2xx_read_reg(dev, attr->index, ®val);
|
||||
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
return snprintf(buf, PAGE_SIZE, "%d\n",
|
||||
ina2xx_get_value(data, attr->index));
|
||||
ina2xx_get_value(data, attr->index, regval));
|
||||
}
|
||||
|
||||
static ssize_t ina2xx_set_shunt(struct device *dev,
|
||||
struct device_attribute *da,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
struct ina2xx_data *data = ina2xx_update_device(dev);
|
||||
unsigned long val;
|
||||
int status;
|
||||
|
||||
if (IS_ERR(data))
|
||||
return PTR_ERR(data);
|
||||
struct ina2xx_data *data = dev_get_drvdata(dev);
|
||||
|
||||
status = kstrtoul(buf, 10, &val);
|
||||
if (status < 0)
|
||||
@ -345,10 +320,10 @@ static ssize_t ina2xx_set_shunt(struct device *dev,
|
||||
val > data->config->calibration_factor)
|
||||
return -EINVAL;
|
||||
|
||||
mutex_lock(&data->update_lock);
|
||||
mutex_lock(&data->config_lock);
|
||||
data->rshunt = val;
|
||||
status = ina2xx_calibrate(data);
|
||||
mutex_unlock(&data->update_lock);
|
||||
mutex_unlock(&data->config_lock);
|
||||
if (status < 0)
|
||||
return status;
|
||||
|
||||
@ -370,17 +345,9 @@ static ssize_t ina226_set_interval(struct device *dev,
|
||||
if (val > INT_MAX || val == 0)
|
||||
return -EINVAL;
|
||||
|
||||
mutex_lock(&data->update_lock);
|
||||
data->curr_config = ina226_interval_to_reg(val,
|
||||
data->regs[INA2XX_CONFIG]);
|
||||
status = i2c_smbus_write_word_swapped(data->client,
|
||||
INA2XX_CONFIG,
|
||||
data->curr_config);
|
||||
|
||||
ina226_set_update_interval(data);
|
||||
/* Make sure the next access re-reads all registers. */
|
||||
data->valid = 0;
|
||||
mutex_unlock(&data->update_lock);
|
||||
status = regmap_update_bits(data->regmap, INA2XX_CONFIG,
|
||||
INA226_AVG_RD_MASK,
|
||||
ina226_interval_to_reg(val));
|
||||
if (status < 0)
|
||||
return status;
|
||||
|
||||
@ -390,18 +357,15 @@ static ssize_t ina226_set_interval(struct device *dev,
|
||||
static ssize_t ina226_show_interval(struct device *dev,
|
||||
struct device_attribute *da, char *buf)
|
||||
{
|
||||
struct ina2xx_data *data = ina2xx_update_device(dev);
|
||||
struct ina2xx_data *data = dev_get_drvdata(dev);
|
||||
int status;
|
||||
unsigned int regval;
|
||||
|
||||
if (IS_ERR(data))
|
||||
return PTR_ERR(data);
|
||||
status = regmap_read(data->regmap, INA2XX_CONFIG, ®val);
|
||||
if (status)
|
||||
return status;
|
||||
|
||||
/*
|
||||
* We don't use data->update_interval here as we want to display
|
||||
* the actual interval used by the chip and jiffies_to_msecs()
|
||||
* doesn't seem to be accurate enough.
|
||||
*/
|
||||
return snprintf(buf, PAGE_SIZE, "%d\n",
|
||||
ina226_reg_to_interval(data->regs[INA2XX_CONFIG]));
|
||||
return snprintf(buf, PAGE_SIZE, "%d\n", ina226_reg_to_interval(regval));
|
||||
}
|
||||
|
||||
/* shunt voltage */
|
||||
@ -455,60 +419,51 @@ static const struct attribute_group ina226_group = {
|
||||
static int ina2xx_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
struct i2c_adapter *adapter = client->adapter;
|
||||
struct ina2xx_platform_data *pdata;
|
||||
struct device *dev = &client->dev;
|
||||
struct ina2xx_data *data;
|
||||
struct device *hwmon_dev;
|
||||
u32 val;
|
||||
int ret, group = 0;
|
||||
|
||||
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_WORD_DATA))
|
||||
return -ENODEV;
|
||||
|
||||
data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
|
||||
if (!data)
|
||||
return -ENOMEM;
|
||||
|
||||
if (dev_get_platdata(dev)) {
|
||||
pdata = dev_get_platdata(dev);
|
||||
data->rshunt = pdata->shunt_uohms;
|
||||
} else if (!of_property_read_u32(dev->of_node,
|
||||
"shunt-resistor", &val)) {
|
||||
data->rshunt = val;
|
||||
} else {
|
||||
data->rshunt = INA2XX_RSHUNT_DEFAULT;
|
||||
/* set the device type */
|
||||
data->config = &ina2xx_config[id->driver_data];
|
||||
|
||||
if (of_property_read_u32(dev->of_node, "shunt-resistor", &val) < 0) {
|
||||
struct ina2xx_platform_data *pdata = dev_get_platdata(dev);
|
||||
|
||||
if (pdata)
|
||||
val = pdata->shunt_uohms;
|
||||
else
|
||||
val = INA2XX_RSHUNT_DEFAULT;
|
||||
}
|
||||
|
||||
/* set the device type */
|
||||
data->kind = id->driver_data;
|
||||
data->config = &ina2xx_config[data->kind];
|
||||
data->curr_config = data->config->config_default;
|
||||
data->client = client;
|
||||
|
||||
/*
|
||||
* Ina226 has a variable update_interval. For ina219 we
|
||||
* use a constant value.
|
||||
*/
|
||||
if (data->kind == ina226)
|
||||
ina226_set_update_interval(data);
|
||||
else
|
||||
data->update_interval = HZ / INA2XX_CONVERSION_RATE;
|
||||
|
||||
if (data->rshunt <= 0 ||
|
||||
data->rshunt > data->config->calibration_factor)
|
||||
if (val <= 0 || val > data->config->calibration_factor)
|
||||
return -ENODEV;
|
||||
|
||||
data->rshunt = val;
|
||||
|
||||
ina2xx_regmap_config.max_register = data->config->registers;
|
||||
|
||||
data->regmap = devm_regmap_init_i2c(client, &ina2xx_regmap_config);
|
||||
if (IS_ERR(data->regmap)) {
|
||||
dev_err(dev, "failed to allocate register map\n");
|
||||
return PTR_ERR(data->regmap);
|
||||
}
|
||||
|
||||
ret = ina2xx_init(data);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "error configuring the device: %d\n", ret);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
mutex_init(&data->update_lock);
|
||||
mutex_init(&data->config_lock);
|
||||
|
||||
data->groups[group++] = &ina2xx_group;
|
||||
if (data->kind == ina226)
|
||||
if (id->driver_data == ina226)
|
||||
data->groups[group++] = &ina226_group;
|
||||
|
||||
hwmon_dev = devm_hwmon_device_register_with_groups(dev, client->name,
|
||||
|
@ -57,6 +57,7 @@ enum lm75_type { /* keep sorted in alphabetical order */
|
||||
tmp175,
|
||||
tmp275,
|
||||
tmp75,
|
||||
tmp75c,
|
||||
};
|
||||
|
||||
/* Addresses scanned */
|
||||
@ -280,6 +281,11 @@ lm75_probe(struct i2c_client *client, const struct i2c_device_id *id)
|
||||
data->resolution = 12;
|
||||
data->sample_time = HZ / 2;
|
||||
break;
|
||||
case tmp75c:
|
||||
clr_mask |= 1 << 5; /* not one-shot mode */
|
||||
data->resolution = 12;
|
||||
data->sample_time = HZ / 4;
|
||||
break;
|
||||
}
|
||||
|
||||
/* configure as specified */
|
||||
@ -343,6 +349,7 @@ static const struct i2c_device_id lm75_ids[] = {
|
||||
{ "tmp175", tmp175, },
|
||||
{ "tmp275", tmp275, },
|
||||
{ "tmp75", tmp75, },
|
||||
{ "tmp75c", tmp75c, },
|
||||
{ /* LIST END */ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, lm75_ids);
|
||||
|
603
drivers/hwmon/max31790.c
Normal file
603
drivers/hwmon/max31790.c
Normal file
@ -0,0 +1,603 @@
|
||||
/*
|
||||
* max31790.c - Part of lm_sensors, Linux kernel modules for hardware
|
||||
* monitoring.
|
||||
*
|
||||
* (C) 2015 by Il Han <corone.il.han@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#include <linux/err.h>
|
||||
#include <linux/hwmon.h>
|
||||
#include <linux/hwmon-sysfs.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/jiffies.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
/* MAX31790 registers */
|
||||
#define MAX31790_REG_GLOBAL_CONFIG 0x00
|
||||
#define MAX31790_REG_FAN_CONFIG(ch) (0x02 + (ch))
|
||||
#define MAX31790_REG_FAN_DYNAMICS(ch) (0x08 + (ch))
|
||||
#define MAX31790_REG_FAN_FAULT_STATUS2 0x10
|
||||
#define MAX31790_REG_FAN_FAULT_STATUS1 0x11
|
||||
#define MAX31790_REG_TACH_COUNT(ch) (0x18 + (ch) * 2)
|
||||
#define MAX31790_REG_PWM_DUTY_CYCLE(ch) (0x30 + (ch) * 2)
|
||||
#define MAX31790_REG_PWMOUT(ch) (0x40 + (ch) * 2)
|
||||
#define MAX31790_REG_TARGET_COUNT(ch) (0x50 + (ch) * 2)
|
||||
|
||||
/* Fan Config register bits */
|
||||
#define MAX31790_FAN_CFG_RPM_MODE 0x80
|
||||
#define MAX31790_FAN_CFG_TACH_INPUT_EN 0x08
|
||||
#define MAX31790_FAN_CFG_TACH_INPUT 0x01
|
||||
|
||||
/* Fan Dynamics register bits */
|
||||
#define MAX31790_FAN_DYN_SR_SHIFT 5
|
||||
#define MAX31790_FAN_DYN_SR_MASK 0xE0
|
||||
#define SR_FROM_REG(reg) (((reg) & MAX31790_FAN_DYN_SR_MASK) \
|
||||
>> MAX31790_FAN_DYN_SR_SHIFT)
|
||||
|
||||
#define FAN_RPM_MIN 120
|
||||
#define FAN_RPM_MAX 7864320
|
||||
|
||||
#define RPM_FROM_REG(reg, sr) (((reg) >> 4) ? \
|
||||
((60 * (sr) * 8192) / ((reg) >> 4)) : \
|
||||
FAN_RPM_MAX)
|
||||
#define RPM_TO_REG(rpm, sr) ((60 * (sr) * 8192) / ((rpm) * 2))
|
||||
|
||||
#define NR_CHANNEL 6
|
||||
|
||||
/*
|
||||
* Client data (each client gets its own)
|
||||
*/
|
||||
struct max31790_data {
|
||||
struct i2c_client *client;
|
||||
struct mutex update_lock;
|
||||
bool valid; /* zero until following fields are valid */
|
||||
unsigned long last_updated; /* in jiffies */
|
||||
|
||||
/* register values */
|
||||
u8 fan_config[NR_CHANNEL];
|
||||
u8 fan_dynamics[NR_CHANNEL];
|
||||
u16 fault_status;
|
||||
u16 tach[NR_CHANNEL * 2];
|
||||
u16 pwm[NR_CHANNEL];
|
||||
u16 target_count[NR_CHANNEL];
|
||||
};
|
||||
|
||||
static struct max31790_data *max31790_update_device(struct device *dev)
|
||||
{
|
||||
struct max31790_data *data = dev_get_drvdata(dev);
|
||||
struct i2c_client *client = data->client;
|
||||
struct max31790_data *ret = data;
|
||||
int i;
|
||||
int rv;
|
||||
|
||||
mutex_lock(&data->update_lock);
|
||||
|
||||
if (time_after(jiffies, data->last_updated + HZ) || !data->valid) {
|
||||
rv = i2c_smbus_read_byte_data(client,
|
||||
MAX31790_REG_FAN_FAULT_STATUS1);
|
||||
if (rv < 0)
|
||||
goto abort;
|
||||
data->fault_status = rv & 0x3F;
|
||||
|
||||
rv = i2c_smbus_read_byte_data(client,
|
||||
MAX31790_REG_FAN_FAULT_STATUS2);
|
||||
if (rv < 0)
|
||||
goto abort;
|
||||
data->fault_status |= (rv & 0x3F) << 6;
|
||||
|
||||
for (i = 0; i < NR_CHANNEL; i++) {
|
||||
rv = i2c_smbus_read_word_swapped(client,
|
||||
MAX31790_REG_TACH_COUNT(i));
|
||||
if (rv < 0)
|
||||
goto abort;
|
||||
data->tach[i] = rv;
|
||||
|
||||
if (data->fan_config[i]
|
||||
& MAX31790_FAN_CFG_TACH_INPUT) {
|
||||
rv = i2c_smbus_read_word_swapped(client,
|
||||
MAX31790_REG_TACH_COUNT(NR_CHANNEL
|
||||
+ i));
|
||||
if (rv < 0)
|
||||
goto abort;
|
||||
data->tach[NR_CHANNEL + i] = rv;
|
||||
} else {
|
||||
rv = i2c_smbus_read_word_swapped(client,
|
||||
MAX31790_REG_PWMOUT(i));
|
||||
if (rv < 0)
|
||||
goto abort;
|
||||
data->pwm[i] = rv;
|
||||
|
||||
rv = i2c_smbus_read_word_swapped(client,
|
||||
MAX31790_REG_TARGET_COUNT(i));
|
||||
if (rv < 0)
|
||||
goto abort;
|
||||
data->target_count[i] = rv;
|
||||
}
|
||||
}
|
||||
|
||||
data->last_updated = jiffies;
|
||||
data->valid = true;
|
||||
}
|
||||
goto done;
|
||||
|
||||
abort:
|
||||
data->valid = false;
|
||||
ret = ERR_PTR(rv);
|
||||
|
||||
done:
|
||||
mutex_unlock(&data->update_lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const u8 tach_period[8] = { 1, 2, 4, 8, 16, 32, 32, 32 };
|
||||
|
||||
static u8 get_tach_period(u8 fan_dynamics)
|
||||
{
|
||||
return tach_period[SR_FROM_REG(fan_dynamics)];
|
||||
}
|
||||
|
||||
static u8 bits_for_tach_period(int rpm)
|
||||
{
|
||||
u8 bits;
|
||||
|
||||
if (rpm < 500)
|
||||
bits = 0x0;
|
||||
else if (rpm < 1000)
|
||||
bits = 0x1;
|
||||
else if (rpm < 2000)
|
||||
bits = 0x2;
|
||||
else if (rpm < 4000)
|
||||
bits = 0x3;
|
||||
else if (rpm < 8000)
|
||||
bits = 0x4;
|
||||
else
|
||||
bits = 0x5;
|
||||
|
||||
return bits;
|
||||
}
|
||||
|
||||
static ssize_t get_fan(struct device *dev,
|
||||
struct device_attribute *devattr, char *buf)
|
||||
{
|
||||
struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
|
||||
struct max31790_data *data = max31790_update_device(dev);
|
||||
int sr, rpm;
|
||||
|
||||
if (IS_ERR(data))
|
||||
return PTR_ERR(data);
|
||||
|
||||
sr = get_tach_period(data->fan_dynamics[attr->index]);
|
||||
rpm = RPM_FROM_REG(data->tach[attr->index], sr);
|
||||
|
||||
return sprintf(buf, "%d\n", rpm);
|
||||
}
|
||||
|
||||
static ssize_t get_fan_target(struct device *dev,
|
||||
struct device_attribute *devattr, char *buf)
|
||||
{
|
||||
struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
|
||||
struct max31790_data *data = max31790_update_device(dev);
|
||||
int sr, rpm;
|
||||
|
||||
if (IS_ERR(data))
|
||||
return PTR_ERR(data);
|
||||
|
||||
sr = get_tach_period(data->fan_dynamics[attr->index]);
|
||||
rpm = RPM_FROM_REG(data->target_count[attr->index], sr);
|
||||
|
||||
return sprintf(buf, "%d\n", rpm);
|
||||
}
|
||||
|
||||
static ssize_t set_fan_target(struct device *dev,
|
||||
struct device_attribute *devattr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
|
||||
struct max31790_data *data = dev_get_drvdata(dev);
|
||||
struct i2c_client *client = data->client;
|
||||
u8 bits;
|
||||
int sr;
|
||||
int target_count;
|
||||
unsigned long rpm;
|
||||
int err;
|
||||
|
||||
err = kstrtoul(buf, 10, &rpm);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
mutex_lock(&data->update_lock);
|
||||
|
||||
rpm = clamp_val(rpm, FAN_RPM_MIN, FAN_RPM_MAX);
|
||||
bits = bits_for_tach_period(rpm);
|
||||
data->fan_dynamics[attr->index] =
|
||||
((data->fan_dynamics[attr->index]
|
||||
& ~MAX31790_FAN_DYN_SR_MASK)
|
||||
| (bits << MAX31790_FAN_DYN_SR_SHIFT));
|
||||
err = i2c_smbus_write_byte_data(client,
|
||||
MAX31790_REG_FAN_DYNAMICS(attr->index),
|
||||
data->fan_dynamics[attr->index]);
|
||||
|
||||
if (err < 0) {
|
||||
mutex_unlock(&data->update_lock);
|
||||
return err;
|
||||
}
|
||||
|
||||
sr = get_tach_period(data->fan_dynamics[attr->index]);
|
||||
target_count = RPM_TO_REG(rpm, sr);
|
||||
target_count = clamp_val(target_count, 0x1, 0x7FF);
|
||||
|
||||
data->target_count[attr->index] = target_count << 5;
|
||||
|
||||
err = i2c_smbus_write_word_swapped(client,
|
||||
MAX31790_REG_TARGET_COUNT(attr->index),
|
||||
data->target_count[attr->index]);
|
||||
|
||||
mutex_unlock(&data->update_lock);
|
||||
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static ssize_t get_pwm(struct device *dev,
|
||||
struct device_attribute *devattr, char *buf)
|
||||
{
|
||||
struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
|
||||
struct max31790_data *data = max31790_update_device(dev);
|
||||
int pwm;
|
||||
|
||||
if (IS_ERR(data))
|
||||
return PTR_ERR(data);
|
||||
|
||||
pwm = data->pwm[attr->index] >> 8;
|
||||
|
||||
return sprintf(buf, "%d\n", pwm);
|
||||
}
|
||||
|
||||
static ssize_t set_pwm(struct device *dev,
|
||||
struct device_attribute *devattr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
|
||||
struct max31790_data *data = dev_get_drvdata(dev);
|
||||
struct i2c_client *client = data->client;
|
||||
unsigned long pwm;
|
||||
int err;
|
||||
|
||||
err = kstrtoul(buf, 10, &pwm);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
if (pwm > 255)
|
||||
return -EINVAL;
|
||||
|
||||
mutex_lock(&data->update_lock);
|
||||
|
||||
data->pwm[attr->index] = pwm << 8;
|
||||
err = i2c_smbus_write_word_swapped(client,
|
||||
MAX31790_REG_PWMOUT(attr->index),
|
||||
data->pwm[attr->index]);
|
||||
|
||||
mutex_unlock(&data->update_lock);
|
||||
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static ssize_t get_pwm_enable(struct device *dev,
|
||||
struct device_attribute *devattr, char *buf)
|
||||
{
|
||||
struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
|
||||
struct max31790_data *data = max31790_update_device(dev);
|
||||
int mode;
|
||||
|
||||
if (IS_ERR(data))
|
||||
return PTR_ERR(data);
|
||||
|
||||
if (data->fan_config[attr->index] & MAX31790_FAN_CFG_RPM_MODE)
|
||||
mode = 2;
|
||||
else if (data->fan_config[attr->index] & MAX31790_FAN_CFG_TACH_INPUT_EN)
|
||||
mode = 1;
|
||||
else
|
||||
mode = 0;
|
||||
|
||||
return sprintf(buf, "%d\n", mode);
|
||||
}
|
||||
|
||||
static ssize_t set_pwm_enable(struct device *dev,
|
||||
struct device_attribute *devattr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
|
||||
struct max31790_data *data = dev_get_drvdata(dev);
|
||||
struct i2c_client *client = data->client;
|
||||
unsigned long mode;
|
||||
int err;
|
||||
|
||||
err = kstrtoul(buf, 10, &mode);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
switch (mode) {
|
||||
case 0:
|
||||
data->fan_config[attr->index] =
|
||||
data->fan_config[attr->index]
|
||||
& ~(MAX31790_FAN_CFG_TACH_INPUT_EN
|
||||
| MAX31790_FAN_CFG_RPM_MODE);
|
||||
break;
|
||||
case 1:
|
||||
data->fan_config[attr->index] =
|
||||
(data->fan_config[attr->index]
|
||||
| MAX31790_FAN_CFG_TACH_INPUT_EN)
|
||||
& ~MAX31790_FAN_CFG_RPM_MODE;
|
||||
break;
|
||||
case 2:
|
||||
data->fan_config[attr->index] =
|
||||
data->fan_config[attr->index]
|
||||
| MAX31790_FAN_CFG_TACH_INPUT_EN
|
||||
| MAX31790_FAN_CFG_RPM_MODE;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
mutex_lock(&data->update_lock);
|
||||
|
||||
err = i2c_smbus_write_byte_data(client,
|
||||
MAX31790_REG_FAN_CONFIG(attr->index),
|
||||
data->fan_config[attr->index]);
|
||||
|
||||
mutex_unlock(&data->update_lock);
|
||||
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static ssize_t get_fan_fault(struct device *dev,
|
||||
struct device_attribute *devattr, char *buf)
|
||||
{
|
||||
struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
|
||||
struct max31790_data *data = max31790_update_device(dev);
|
||||
int fault;
|
||||
|
||||
if (IS_ERR(data))
|
||||
return PTR_ERR(data);
|
||||
|
||||
fault = !!(data->fault_status & (1 << attr->index));
|
||||
|
||||
return sprintf(buf, "%d\n", fault);
|
||||
}
|
||||
|
||||
static SENSOR_DEVICE_ATTR(fan1_input, S_IRUGO, get_fan, NULL, 0);
|
||||
static SENSOR_DEVICE_ATTR(fan2_input, S_IRUGO, get_fan, NULL, 1);
|
||||
static SENSOR_DEVICE_ATTR(fan3_input, S_IRUGO, get_fan, NULL, 2);
|
||||
static SENSOR_DEVICE_ATTR(fan4_input, S_IRUGO, get_fan, NULL, 3);
|
||||
static SENSOR_DEVICE_ATTR(fan5_input, S_IRUGO, get_fan, NULL, 4);
|
||||
static SENSOR_DEVICE_ATTR(fan6_input, S_IRUGO, get_fan, NULL, 5);
|
||||
|
||||
static SENSOR_DEVICE_ATTR(fan1_fault, S_IRUGO, get_fan_fault, NULL, 0);
|
||||
static SENSOR_DEVICE_ATTR(fan2_fault, S_IRUGO, get_fan_fault, NULL, 1);
|
||||
static SENSOR_DEVICE_ATTR(fan3_fault, S_IRUGO, get_fan_fault, NULL, 2);
|
||||
static SENSOR_DEVICE_ATTR(fan4_fault, S_IRUGO, get_fan_fault, NULL, 3);
|
||||
static SENSOR_DEVICE_ATTR(fan5_fault, S_IRUGO, get_fan_fault, NULL, 4);
|
||||
static SENSOR_DEVICE_ATTR(fan6_fault, S_IRUGO, get_fan_fault, NULL, 5);
|
||||
|
||||
static SENSOR_DEVICE_ATTR(fan7_input, S_IRUGO, get_fan, NULL, 6);
|
||||
static SENSOR_DEVICE_ATTR(fan8_input, S_IRUGO, get_fan, NULL, 7);
|
||||
static SENSOR_DEVICE_ATTR(fan9_input, S_IRUGO, get_fan, NULL, 8);
|
||||
static SENSOR_DEVICE_ATTR(fan10_input, S_IRUGO, get_fan, NULL, 9);
|
||||
static SENSOR_DEVICE_ATTR(fan11_input, S_IRUGO, get_fan, NULL, 10);
|
||||
static SENSOR_DEVICE_ATTR(fan12_input, S_IRUGO, get_fan, NULL, 11);
|
||||
|
||||
static SENSOR_DEVICE_ATTR(fan7_fault, S_IRUGO, get_fan_fault, NULL, 6);
|
||||
static SENSOR_DEVICE_ATTR(fan8_fault, S_IRUGO, get_fan_fault, NULL, 7);
|
||||
static SENSOR_DEVICE_ATTR(fan9_fault, S_IRUGO, get_fan_fault, NULL, 8);
|
||||
static SENSOR_DEVICE_ATTR(fan10_fault, S_IRUGO, get_fan_fault, NULL, 9);
|
||||
static SENSOR_DEVICE_ATTR(fan11_fault, S_IRUGO, get_fan_fault, NULL, 10);
|
||||
static SENSOR_DEVICE_ATTR(fan12_fault, S_IRUGO, get_fan_fault, NULL, 11);
|
||||
|
||||
static SENSOR_DEVICE_ATTR(fan1_target, S_IWUSR | S_IRUGO,
|
||||
get_fan_target, set_fan_target, 0);
|
||||
static SENSOR_DEVICE_ATTR(fan2_target, S_IWUSR | S_IRUGO,
|
||||
get_fan_target, set_fan_target, 1);
|
||||
static SENSOR_DEVICE_ATTR(fan3_target, S_IWUSR | S_IRUGO,
|
||||
get_fan_target, set_fan_target, 2);
|
||||
static SENSOR_DEVICE_ATTR(fan4_target, S_IWUSR | S_IRUGO,
|
||||
get_fan_target, set_fan_target, 3);
|
||||
static SENSOR_DEVICE_ATTR(fan5_target, S_IWUSR | S_IRUGO,
|
||||
get_fan_target, set_fan_target, 4);
|
||||
static SENSOR_DEVICE_ATTR(fan6_target, S_IWUSR | S_IRUGO,
|
||||
get_fan_target, set_fan_target, 5);
|
||||
|
||||
static SENSOR_DEVICE_ATTR(pwm1, S_IWUSR | S_IRUGO, get_pwm, set_pwm, 0);
|
||||
static SENSOR_DEVICE_ATTR(pwm2, S_IWUSR | S_IRUGO, get_pwm, set_pwm, 1);
|
||||
static SENSOR_DEVICE_ATTR(pwm3, S_IWUSR | S_IRUGO, get_pwm, set_pwm, 2);
|
||||
static SENSOR_DEVICE_ATTR(pwm4, S_IWUSR | S_IRUGO, get_pwm, set_pwm, 3);
|
||||
static SENSOR_DEVICE_ATTR(pwm5, S_IWUSR | S_IRUGO, get_pwm, set_pwm, 4);
|
||||
static SENSOR_DEVICE_ATTR(pwm6, S_IWUSR | S_IRUGO, get_pwm, set_pwm, 5);
|
||||
|
||||
static SENSOR_DEVICE_ATTR(pwm1_enable, S_IWUSR | S_IRUGO,
|
||||
get_pwm_enable, set_pwm_enable, 0);
|
||||
static SENSOR_DEVICE_ATTR(pwm2_enable, S_IWUSR | S_IRUGO,
|
||||
get_pwm_enable, set_pwm_enable, 1);
|
||||
static SENSOR_DEVICE_ATTR(pwm3_enable, S_IWUSR | S_IRUGO,
|
||||
get_pwm_enable, set_pwm_enable, 2);
|
||||
static SENSOR_DEVICE_ATTR(pwm4_enable, S_IWUSR | S_IRUGO,
|
||||
get_pwm_enable, set_pwm_enable, 3);
|
||||
static SENSOR_DEVICE_ATTR(pwm5_enable, S_IWUSR | S_IRUGO,
|
||||
get_pwm_enable, set_pwm_enable, 4);
|
||||
static SENSOR_DEVICE_ATTR(pwm6_enable, S_IWUSR | S_IRUGO,
|
||||
get_pwm_enable, set_pwm_enable, 5);
|
||||
|
||||
static struct attribute *max31790_attrs[] = {
|
||||
&sensor_dev_attr_fan1_input.dev_attr.attr,
|
||||
&sensor_dev_attr_fan2_input.dev_attr.attr,
|
||||
&sensor_dev_attr_fan3_input.dev_attr.attr,
|
||||
&sensor_dev_attr_fan4_input.dev_attr.attr,
|
||||
&sensor_dev_attr_fan5_input.dev_attr.attr,
|
||||
&sensor_dev_attr_fan6_input.dev_attr.attr,
|
||||
|
||||
&sensor_dev_attr_fan1_fault.dev_attr.attr,
|
||||
&sensor_dev_attr_fan2_fault.dev_attr.attr,
|
||||
&sensor_dev_attr_fan3_fault.dev_attr.attr,
|
||||
&sensor_dev_attr_fan4_fault.dev_attr.attr,
|
||||
&sensor_dev_attr_fan5_fault.dev_attr.attr,
|
||||
&sensor_dev_attr_fan6_fault.dev_attr.attr,
|
||||
|
||||
&sensor_dev_attr_fan7_input.dev_attr.attr,
|
||||
&sensor_dev_attr_fan8_input.dev_attr.attr,
|
||||
&sensor_dev_attr_fan9_input.dev_attr.attr,
|
||||
&sensor_dev_attr_fan10_input.dev_attr.attr,
|
||||
&sensor_dev_attr_fan11_input.dev_attr.attr,
|
||||
&sensor_dev_attr_fan12_input.dev_attr.attr,
|
||||
|
||||
&sensor_dev_attr_fan7_fault.dev_attr.attr,
|
||||
&sensor_dev_attr_fan8_fault.dev_attr.attr,
|
||||
&sensor_dev_attr_fan9_fault.dev_attr.attr,
|
||||
&sensor_dev_attr_fan10_fault.dev_attr.attr,
|
||||
&sensor_dev_attr_fan11_fault.dev_attr.attr,
|
||||
&sensor_dev_attr_fan12_fault.dev_attr.attr,
|
||||
|
||||
&sensor_dev_attr_fan1_target.dev_attr.attr,
|
||||
&sensor_dev_attr_fan2_target.dev_attr.attr,
|
||||
&sensor_dev_attr_fan3_target.dev_attr.attr,
|
||||
&sensor_dev_attr_fan4_target.dev_attr.attr,
|
||||
&sensor_dev_attr_fan5_target.dev_attr.attr,
|
||||
&sensor_dev_attr_fan6_target.dev_attr.attr,
|
||||
|
||||
&sensor_dev_attr_pwm1.dev_attr.attr,
|
||||
&sensor_dev_attr_pwm2.dev_attr.attr,
|
||||
&sensor_dev_attr_pwm3.dev_attr.attr,
|
||||
&sensor_dev_attr_pwm4.dev_attr.attr,
|
||||
&sensor_dev_attr_pwm5.dev_attr.attr,
|
||||
&sensor_dev_attr_pwm6.dev_attr.attr,
|
||||
|
||||
&sensor_dev_attr_pwm1_enable.dev_attr.attr,
|
||||
&sensor_dev_attr_pwm2_enable.dev_attr.attr,
|
||||
&sensor_dev_attr_pwm3_enable.dev_attr.attr,
|
||||
&sensor_dev_attr_pwm4_enable.dev_attr.attr,
|
||||
&sensor_dev_attr_pwm5_enable.dev_attr.attr,
|
||||
&sensor_dev_attr_pwm6_enable.dev_attr.attr,
|
||||
NULL
|
||||
};
|
||||
|
||||
static umode_t max31790_attrs_visible(struct kobject *kobj,
|
||||
struct attribute *a, int n)
|
||||
{
|
||||
struct device *dev = container_of(kobj, struct device, kobj);
|
||||
struct max31790_data *data = dev_get_drvdata(dev);
|
||||
struct device_attribute *devattr =
|
||||
container_of(a, struct device_attribute, attr);
|
||||
int index = to_sensor_dev_attr(devattr)->index % NR_CHANNEL;
|
||||
u8 fan_config;
|
||||
|
||||
fan_config = data->fan_config[index];
|
||||
|
||||
if (n >= NR_CHANNEL * 2 && n < NR_CHANNEL * 4 &&
|
||||
!(fan_config & MAX31790_FAN_CFG_TACH_INPUT))
|
||||
return 0;
|
||||
if (n >= NR_CHANNEL * 4 && (fan_config & MAX31790_FAN_CFG_TACH_INPUT))
|
||||
return 0;
|
||||
|
||||
return a->mode;
|
||||
}
|
||||
|
||||
static const struct attribute_group max31790_group = {
|
||||
.attrs = max31790_attrs,
|
||||
.is_visible = max31790_attrs_visible,
|
||||
};
|
||||
__ATTRIBUTE_GROUPS(max31790);
|
||||
|
||||
static int max31790_init_client(struct i2c_client *client,
|
||||
struct max31790_data *data)
|
||||
{
|
||||
int i, rv;
|
||||
|
||||
for (i = 0; i < NR_CHANNEL; i++) {
|
||||
rv = i2c_smbus_read_byte_data(client,
|
||||
MAX31790_REG_FAN_CONFIG(i));
|
||||
if (rv < 0)
|
||||
return rv;
|
||||
data->fan_config[i] = rv;
|
||||
|
||||
rv = i2c_smbus_read_byte_data(client,
|
||||
MAX31790_REG_FAN_DYNAMICS(i));
|
||||
if (rv < 0)
|
||||
return rv;
|
||||
data->fan_dynamics[i] = rv;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int max31790_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
struct i2c_adapter *adapter = client->adapter;
|
||||
struct device *dev = &client->dev;
|
||||
struct max31790_data *data;
|
||||
struct device *hwmon_dev;
|
||||
int err;
|
||||
|
||||
if (!i2c_check_functionality(adapter,
|
||||
I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA))
|
||||
return -ENODEV;
|
||||
|
||||
data = devm_kzalloc(dev, sizeof(struct max31790_data), GFP_KERNEL);
|
||||
if (!data)
|
||||
return -ENOMEM;
|
||||
|
||||
data->client = client;
|
||||
mutex_init(&data->update_lock);
|
||||
|
||||
/*
|
||||
* Initialize the max31790 chip
|
||||
*/
|
||||
err = max31790_init_client(client, data);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
hwmon_dev = devm_hwmon_device_register_with_groups(dev,
|
||||
client->name, data, max31790_groups);
|
||||
|
||||
return PTR_ERR_OR_ZERO(hwmon_dev);
|
||||
}
|
||||
|
||||
static const struct i2c_device_id max31790_id[] = {
|
||||
{ "max31790", 0 },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, max31790_id);
|
||||
|
||||
static struct i2c_driver max31790_driver = {
|
||||
.class = I2C_CLASS_HWMON,
|
||||
.probe = max31790_probe,
|
||||
.driver = {
|
||||
.name = "max31790",
|
||||
},
|
||||
.id_table = max31790_id,
|
||||
};
|
||||
|
||||
module_i2c_driver(max31790_driver);
|
||||
|
||||
MODULE_AUTHOR("Il Han <corone.il.han@gmail.com>");
|
||||
MODULE_DESCRIPTION("MAX31790 sensor driver");
|
||||
MODULE_LICENSE("GPL");
|
@ -515,16 +515,24 @@ static const char *const nct6779_temp_label[] = {
|
||||
"PCH_DIM1_TEMP",
|
||||
"PCH_DIM2_TEMP",
|
||||
"PCH_DIM3_TEMP",
|
||||
"BYTE_TEMP"
|
||||
"BYTE_TEMP",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"Virtual_TEMP"
|
||||
};
|
||||
|
||||
static const u16 NCT6779_REG_TEMP_ALTERNATE[ARRAY_SIZE(nct6779_temp_label) - 1]
|
||||
#define NCT6779_NUM_LABELS (ARRAY_SIZE(nct6779_temp_label) - 5)
|
||||
#define NCT6791_NUM_LABELS ARRAY_SIZE(nct6779_temp_label)
|
||||
|
||||
static const u16 NCT6779_REG_TEMP_ALTERNATE[NCT6791_NUM_LABELS - 1]
|
||||
= { 0x490, 0x491, 0x492, 0x493, 0x494, 0x495, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0x400, 0x401, 0x402, 0x404, 0x405, 0x406, 0x407,
|
||||
0x408, 0 };
|
||||
|
||||
static const u16 NCT6779_REG_TEMP_CRIT[ARRAY_SIZE(nct6779_temp_label) - 1]
|
||||
static const u16 NCT6779_REG_TEMP_CRIT[NCT6791_NUM_LABELS - 1]
|
||||
= { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x709, 0x70a };
|
||||
|
||||
/* NCT6791 specific data */
|
||||
@ -557,6 +565,76 @@ static const u16 NCT6792_REG_TEMP_MON[] = {
|
||||
static const u16 NCT6792_REG_BEEP[NUM_REG_BEEP] = {
|
||||
0xb2, 0xb3, 0xb4, 0xb5, 0xbf };
|
||||
|
||||
static const char *const nct6792_temp_label[] = {
|
||||
"",
|
||||
"SYSTIN",
|
||||
"CPUTIN",
|
||||
"AUXTIN0",
|
||||
"AUXTIN1",
|
||||
"AUXTIN2",
|
||||
"AUXTIN3",
|
||||
"",
|
||||
"SMBUSMASTER 0",
|
||||
"SMBUSMASTER 1",
|
||||
"SMBUSMASTER 2",
|
||||
"SMBUSMASTER 3",
|
||||
"SMBUSMASTER 4",
|
||||
"SMBUSMASTER 5",
|
||||
"SMBUSMASTER 6",
|
||||
"SMBUSMASTER 7",
|
||||
"PECI Agent 0",
|
||||
"PECI Agent 1",
|
||||
"PCH_CHIP_CPU_MAX_TEMP",
|
||||
"PCH_CHIP_TEMP",
|
||||
"PCH_CPU_TEMP",
|
||||
"PCH_MCH_TEMP",
|
||||
"PCH_DIM0_TEMP",
|
||||
"PCH_DIM1_TEMP",
|
||||
"PCH_DIM2_TEMP",
|
||||
"PCH_DIM3_TEMP",
|
||||
"BYTE_TEMP",
|
||||
"PECI Agent 0 Calibration",
|
||||
"PECI Agent 1 Calibration",
|
||||
"",
|
||||
"",
|
||||
"Virtual_TEMP"
|
||||
};
|
||||
|
||||
static const char *const nct6793_temp_label[] = {
|
||||
"",
|
||||
"SYSTIN",
|
||||
"CPUTIN",
|
||||
"AUXTIN0",
|
||||
"AUXTIN1",
|
||||
"AUXTIN2",
|
||||
"AUXTIN3",
|
||||
"",
|
||||
"SMBUSMASTER 0",
|
||||
"SMBUSMASTER 1",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"PECI Agent 0",
|
||||
"PECI Agent 1",
|
||||
"PCH_CHIP_CPU_MAX_TEMP",
|
||||
"PCH_CHIP_TEMP",
|
||||
"PCH_CPU_TEMP",
|
||||
"PCH_MCH_TEMP",
|
||||
"Agent0 Dimm0 ",
|
||||
"Agent0 Dimm1",
|
||||
"Agent1 Dimm0",
|
||||
"Agent1 Dimm1",
|
||||
"BYTE_TEMP0",
|
||||
"BYTE_TEMP1",
|
||||
"PECI Agent 0 Calibration",
|
||||
"PECI Agent 1 Calibration",
|
||||
"",
|
||||
"Virtual_TEMP"
|
||||
};
|
||||
|
||||
/* NCT6102D/NCT6106D specific data */
|
||||
|
||||
#define NCT6106_REG_VBAT 0x318
|
||||
@ -3605,7 +3683,7 @@ static int nct6775_probe(struct platform_device *pdev)
|
||||
data->speed_tolerance_limit = 63;
|
||||
|
||||
data->temp_label = nct6779_temp_label;
|
||||
data->temp_label_num = ARRAY_SIZE(nct6779_temp_label);
|
||||
data->temp_label_num = NCT6779_NUM_LABELS;
|
||||
|
||||
data->REG_CONFIG = NCT6775_REG_CONFIG;
|
||||
data->REG_VBAT = NCT6775_REG_VBAT;
|
||||
@ -3682,8 +3760,19 @@ static int nct6775_probe(struct platform_device *pdev)
|
||||
data->tolerance_mask = 0x07;
|
||||
data->speed_tolerance_limit = 63;
|
||||
|
||||
data->temp_label = nct6779_temp_label;
|
||||
data->temp_label_num = ARRAY_SIZE(nct6779_temp_label);
|
||||
switch (data->kind) {
|
||||
default:
|
||||
case nct6791:
|
||||
data->temp_label = nct6779_temp_label;
|
||||
break;
|
||||
case nct6792:
|
||||
data->temp_label = nct6792_temp_label;
|
||||
break;
|
||||
case nct6793:
|
||||
data->temp_label = nct6793_temp_label;
|
||||
break;
|
||||
}
|
||||
data->temp_label_num = NCT6791_NUM_LABELS;
|
||||
|
||||
data->REG_CONFIG = NCT6775_REG_CONFIG;
|
||||
data->REG_VBAT = NCT6775_REG_VBAT;
|
||||
|
Loading…
x
Reference in New Issue
Block a user