Commit 64b08df4 authored by Linus Torvalds's avatar Linus Torvalds

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

Pull hwmon updates from Guenter Roeck:

 - New drivers for Infineon PXE1610 and IRPS5401

 - Minor improvements, cleanup, and fixes in several drivers

* tag 'hwmon-for-v5.3' of git://git.kernel.org/pub/scm/linux/kernel/git/groeck/linux-staging: (33 commits)
  hwmon: (ina3221) Add of_node_put() before return
  hwmon: (gpio-fan) fix sysfs notifications and udev events for gpio-fan alarms
  hwmon: (gpio-fan) move fan_alarm_init after devm_hwmon_device_register_with_groups
  hwmon: (lm90) Introduce function to update configuration register
  hwmon: (lm90) Cache configuration register value
  hwmon: (lm90) Fix max6658 sporadic wrong temperature reading
  hwmon: (nct7904) Changes comments in probe function.
  hwmon: (nct7904) Add error handling in probe function.
  hwmon: Convert remaining drivers to use SPDX identifier
  hwmon: (max6650) Fix unused variable warning
  hwmon: (pmbus/adm1275) Fix power sampling support
  hwmon: (lm90) simplify getting the adapter of a client
  hwmon: (asus_atk0110) no need to check return value of debugfs_create functions
  hwmon: (max6650) Fix minor formatting issues
  hwmon: (max6650) Improve error handling in max6650_update_device
  hwmon: (max6650) Read non-volatile registers only once
  hwmon: (max6650) Convert to use devm_hwmon_device_register_with_info
  hwmon: (max6650) Simplify alarm handling
  hwmon: (max6650) Cache alarm_en register
  hwmon: (max6650) Declare valid as boolean
  ...
parents c079512a 9f754657
Kernel driver pxe1610
=====================
Supported chips:
* Infineon PXE1610
Prefix: 'pxe1610'
Addresses scanned: -
Datasheet: Datasheet is not publicly available.
* Infineon PXE1110
Prefix: 'pxe1110'
Addresses scanned: -
Datasheet: Datasheet is not publicly available.
* Infineon PXM1310
Prefix: 'pxm1310'
Addresses scanned: -
Datasheet: Datasheet is not publicly available.
Author: Vijay Khemka <vijaykhemka@fb.com>
Description
-----------
PXE1610/PXE1110 are Multi-rail/Multiphase Digital Controllers
and compliant to
-- Intel VR13 DC-DC converter specifications.
-- Intel SVID protocol.
Used for Vcore power regulation for Intel VR13 based microprocessors
-- Servers, Workstations, and High-end desktops
PXM1310 is a Multi-rail Controller and it is compliant to
-- Intel VR13 DC-DC converter specifications.
-- Intel SVID protocol.
Used for DDR3/DDR4 Memory power regulation for Intel VR13 and
IMVP8 based systems
Usage Notes
-----------
This driver does not probe for PMBus devices. You will have
to instantiate devices explicitly.
Example: the following commands will load the driver for an PXE1610
at address 0x70 on I2C bus #4:
# modprobe pxe1610
# echo pxe1610 0x70 > /sys/bus/i2c/devices/i2c-4/new_device
It can also be instantiated by declaring in device tree
Sysfs attributes
----------------
curr1_label "iin"
curr1_input Measured input current
curr1_alarm Current high alarm
curr[2-4]_label "iout[1-3]"
curr[2-4]_input Measured output current
curr[2-4]_crit Critical maximum current
curr[2-4]_crit_alarm Current critical high alarm
in1_label "vin"
in1_input Measured input voltage
in1_crit Critical maximum input voltage
in1_crit_alarm Input voltage critical high alarm
in[2-4]_label "vout[1-3]"
in[2-4]_input Measured output voltage
in[2-4]_lcrit Critical minimum output voltage
in[2-4]_lcrit_alarm Output voltage critical low alarm
in[2-4]_crit Critical maximum output voltage
in[2-4]_crit_alarm Output voltage critical high alarm
power1_label "pin"
power1_input Measured input power
power1_alarm Input power high alarm
power[2-4]_label "pout[1-3]"
power[2-4]_input Measured output power
temp[1-3]_input Measured temperature
temp[1-3]_crit Critical high temperature
temp[1-3]_crit_alarm Chip temperature critical high alarm
temp[1-3]_max Maximum temperature
temp[1-3]_max_alarm Chip temperature high alarm
......@@ -10,16 +10,6 @@
* Very rare chip please let me know if you use it
*
* http://www.analog.com/UploadedFiles/Data_Sheets/ADM1029.pdf
*
*
* 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 version 2 of the License
*
* 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/module.h>
......
......@@ -789,33 +789,16 @@ static const struct file_operations atk_debugfs_ggrp_fops = {
static void atk_debugfs_init(struct atk_data *data)
{
struct dentry *d;
struct dentry *f;
data->debugfs.id = 0;
d = debugfs_create_dir("asus_atk0110", NULL);
if (!d || IS_ERR(d))
return;
f = debugfs_create_x32("id", 0600, d, &data->debugfs.id);
if (!f || IS_ERR(f))
goto cleanup;
f = debugfs_create_file_unsafe("gitm", 0400, d, data,
&atk_debugfs_gitm);
if (!f || IS_ERR(f))
goto cleanup;
f = debugfs_create_file("ggrp", 0400, d, data,
&atk_debugfs_ggrp_fops);
if (!f || IS_ERR(f))
goto cleanup;
debugfs_create_x32("id", 0600, d, &data->debugfs.id);
debugfs_create_file_unsafe("gitm", 0400, d, data, &atk_debugfs_gitm);
debugfs_create_file("ggrp", 0400, d, data, &atk_debugfs_ggrp_fops);
data->debugfs.root = d;
return;
cleanup:
debugfs_remove_recursive(d);
}
static void atk_debugfs_cleanup(struct atk_data *data)
......
......@@ -54,8 +54,8 @@ static void fan_alarm_notify(struct work_struct *ws)
struct gpio_fan_data *fan_data =
container_of(ws, struct gpio_fan_data, alarm_work);
sysfs_notify(&fan_data->dev->kobj, NULL, "fan1_alarm");
kobject_uevent(&fan_data->dev->kobj, KOBJ_CHANGE);
sysfs_notify(&fan_data->hwmon_dev->kobj, NULL, "fan1_alarm");
kobject_uevent(&fan_data->hwmon_dev->kobj, KOBJ_CHANGE);
}
static irqreturn_t fan_alarm_irq_handler(int irq, void *dev_id)
......@@ -510,13 +510,6 @@ static int gpio_fan_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, fan_data);
mutex_init(&fan_data->lock);
/* Configure alarm GPIO if available. */
if (fan_data->alarm_gpio) {
err = fan_alarm_init(fan_data);
if (err)
return err;
}
/* Configure control GPIOs if available. */
if (fan_data->gpios && fan_data->num_gpios > 0) {
if (!fan_data->speed || fan_data->num_speed <= 1)
......@@ -524,7 +517,9 @@ static int gpio_fan_probe(struct platform_device *pdev)
err = fan_ctrl_init(fan_data);
if (err)
return err;
devm_add_action_or_reset(dev, gpio_fan_stop, fan_data);
err = devm_add_action_or_reset(dev, gpio_fan_stop, fan_data);
if (err)
return err;
}
/* Make this driver part of hwmon class. */
......@@ -535,6 +530,13 @@ static int gpio_fan_probe(struct platform_device *pdev)
if (IS_ERR(fan_data->hwmon_dev))
return PTR_ERR(fan_data->hwmon_dev);
/* Configure alarm GPIO if available. */
if (fan_data->alarm_gpio) {
err = fan_alarm_init(fan_data);
if (err)
return err;
}
/* Optional cooling device register for Device tree platforms */
fan_data->cdev = devm_thermal_of_cooling_device_register(dev, np,
"gpio-fan", fan_data, &gpio_fan_cool_ops);
......
......@@ -651,6 +651,12 @@ __hwmon_device_register(struct device *dev, const char *name, void *drvdata,
hwdev, j);
if (err) {
device_unregister(hdev);
/*
* Don't worry about hwdev;
* hwmon_dev_release(), called
* from device_unregister(),
* will free it.
*/
goto ida_remove;
}
}
......
......@@ -713,8 +713,10 @@ static int ina3221_probe_from_dt(struct device *dev, struct ina3221_data *ina)
for_each_child_of_node(np, child) {
ret = ina3221_probe_child_from_dt(dev, child, ina);
if (ret)
if (ret) {
of_node_put(child);
return ret;
}
}
return 0;
......
......@@ -174,6 +174,7 @@ enum chips { lm90, adm1032, lm99, lm86, max6657, max6659, adt7461, max6680,
#define LM90_HAVE_EMERGENCY_ALARM (1 << 5)/* emergency alarm */
#define LM90_HAVE_TEMP3 (1 << 6) /* 3rd temperature sensor */
#define LM90_HAVE_BROKEN_ALERT (1 << 7) /* Broken alert */
#define LM90_PAUSE_FOR_CONFIG (1 << 8) /* Pause conversion for config */
/* LM90 status */
#define LM90_STATUS_LTHRM (1 << 0) /* local THERM limit tripped */
......@@ -367,6 +368,7 @@ static const struct lm90_params lm90_params[] = {
.reg_local_ext = MAX6657_REG_R_LOCAL_TEMPL,
},
[max6657] = {
.flags = LM90_PAUSE_FOR_CONFIG,
.alert_alarms = 0x7c,
.max_convrate = 8,
.reg_local_ext = MAX6657_REG_R_LOCAL_TEMPL,
......@@ -457,6 +459,7 @@ struct lm90_data {
unsigned int update_interval; /* in milliseconds */
u8 config; /* Current configuration register value */
u8 config_orig; /* Original configuration register value */
u8 convrate_orig; /* Original conversion rate register value */
u16 alert_alarms; /* Which alarm bits trigger ALERT# */
......@@ -540,6 +543,21 @@ static int lm90_read16(struct i2c_client *client, u8 regh, u8 regl)
return (newh << 8) | l;
}
static int lm90_update_confreg(struct lm90_data *data, u8 config)
{
if (data->config != config) {
int err;
err = i2c_smbus_write_byte_data(data->client,
LM90_REG_W_CONFIG1,
config);
if (err)
return err;
data->config = config;
}
return 0;
}
/*
* client->update_lock must be held when calling this function (unless we are
* in detection or initialization steps), and while a remote channel other
......@@ -548,23 +566,39 @@ static int lm90_read16(struct i2c_client *client, u8 regh, u8 regl)
* various registers have different meanings as a result of selecting a
* non-default remote channel.
*/
static inline int lm90_select_remote_channel(struct i2c_client *client,
struct lm90_data *data,
int channel)
static int lm90_select_remote_channel(struct lm90_data *data, int channel)
{
int config;
int err = 0;
if (data->kind == max6696) {
config = lm90_read_reg(client, LM90_REG_R_CONFIG1);
if (config < 0)
return config;
config &= ~0x08;
u8 config = data->config & ~0x08;
if (channel)
config |= 0x08;
i2c_smbus_write_byte_data(client, LM90_REG_W_CONFIG1,
config);
err = lm90_update_confreg(data, config);
}
return 0;
return err;
}
static int lm90_write_convrate(struct lm90_data *data, int val)
{
u8 config = data->config;
int err;
/* Save config and pause conversion */
if (data->flags & LM90_PAUSE_FOR_CONFIG) {
err = lm90_update_confreg(data, config | 0x40);
if (err < 0)
return err;
}
/* Set conv rate */
err = i2c_smbus_write_byte_data(data->client, LM90_REG_W_CONVRATE, val);
/* Revert change to config */
lm90_update_confreg(data, config);
return err;
}
/*
......@@ -587,7 +621,7 @@ static int lm90_set_convrate(struct i2c_client *client, struct lm90_data *data,
if (interval >= update_interval * 3 / 4)
break;
err = i2c_smbus_write_byte_data(client, LM90_REG_W_CONVRATE, i);
err = lm90_write_convrate(data, i);
data->update_interval = DIV_ROUND_CLOSEST(update_interval, 64);
return err;
}
......@@ -658,7 +692,7 @@ static int lm90_update_limits(struct device *dev)
}
if (data->kind == max6696) {
val = lm90_select_remote_channel(client, data, 1);
val = lm90_select_remote_channel(data, 1);
if (val < 0)
return val;
......@@ -682,7 +716,7 @@ static int lm90_update_limits(struct device *dev)
return val;
data->temp11[REMOTE2_HIGH] = val << 8;
lm90_select_remote_channel(client, data, 0);
lm90_select_remote_channel(data, 0);
}
return 0;
......@@ -742,19 +776,19 @@ static int lm90_update_device(struct device *dev)
data->alarms = val; /* lower 8 bit of alarms */
if (data->kind == max6696) {
val = lm90_select_remote_channel(client, data, 1);
val = lm90_select_remote_channel(data, 1);
if (val < 0)
return val;
val = lm90_read16(client, LM90_REG_R_REMOTE_TEMPH,
LM90_REG_R_REMOTE_TEMPL);
if (val < 0) {
lm90_select_remote_channel(client, data, 0);
lm90_select_remote_channel(data, 0);
return val;
}
data->temp11[REMOTE2_TEMP] = val;
lm90_select_remote_channel(client, data, 0);
lm90_select_remote_channel(data, 0);
val = lm90_read_reg(client, MAX6696_REG_R_STATUS2);
if (val < 0)
......@@ -768,15 +802,9 @@ static int lm90_update_device(struct device *dev)
*/
if (!(data->config_orig & 0x80) &&
!(data->alarms & data->alert_alarms)) {
val = lm90_read_reg(client, LM90_REG_R_CONFIG1);
if (val < 0)
return val;
if (val & 0x80) {
if (data->config & 0x80) {
dev_dbg(&client->dev, "Re-enabling ALERT#\n");
i2c_smbus_write_byte_data(client,
LM90_REG_W_CONFIG1,
val & ~0x80);
lm90_update_confreg(data, data->config & ~0x80);
}
}
......@@ -994,7 +1022,7 @@ static int lm90_set_temp11(struct lm90_data *data, int index, long val)
else
data->temp11[index] = temp_to_s8(val) << 8;
lm90_select_remote_channel(client, data, index >= 3);
lm90_select_remote_channel(data, index >= 3);
err = i2c_smbus_write_byte_data(client, regp->high,
data->temp11[index] >> 8);
if (err < 0)
......@@ -1003,7 +1031,7 @@ static int lm90_set_temp11(struct lm90_data *data, int index, long val)
err = i2c_smbus_write_byte_data(client, regp->low,
data->temp11[index] & 0xff);
lm90_select_remote_channel(client, data, 0);
lm90_select_remote_channel(data, 0);
return err;
}
......@@ -1052,9 +1080,9 @@ static int lm90_set_temp8(struct lm90_data *data, int index, long val)
else
data->temp8[index] = temp_to_s8(val);
lm90_select_remote_channel(client, data, index >= 6);
lm90_select_remote_channel(data, index >= 6);
err = i2c_smbus_write_byte_data(client, reg[index], data->temp8[index]);
lm90_select_remote_channel(client, data, 0);
lm90_select_remote_channel(data, 0);
return err;
}
......@@ -1593,8 +1621,7 @@ static void lm90_restore_conf(void *_data)
struct i2c_client *client = data->client;
/* Restore initial configuration */
i2c_smbus_write_byte_data(client, LM90_REG_W_CONVRATE,
data->convrate_orig);
lm90_write_convrate(data, data->convrate_orig);
i2c_smbus_write_byte_data(client, LM90_REG_W_CONFIG1,
data->config_orig);
}
......@@ -1611,11 +1638,13 @@ static int lm90_init_client(struct i2c_client *client, struct lm90_data *data)
/*
* Start the conversions.
*/
lm90_set_convrate(client, data, 500); /* 500ms; 2Hz conversion rate */
config = lm90_read_reg(client, LM90_REG_R_CONFIG1);
if (config < 0)
return config;
data->config_orig = config;
data->config = config;
lm90_set_convrate(client, data, 500); /* 500ms; 2Hz conversion rate */
/* Check Temperature Range Select */
if (data->kind == adt7461 || data->kind == tmp451) {
......@@ -1638,8 +1667,7 @@ static int lm90_init_client(struct i2c_client *client, struct lm90_data *data)
config &= ~0x08;
config &= 0xBF; /* run */
if (config != data->config_orig) /* Only write if changed */
i2c_smbus_write_byte_data(client, LM90_REG_W_CONFIG1, config);
lm90_update_confreg(data, config);
return devm_add_action_or_reset(&client->dev, lm90_restore_conf, data);
}
......@@ -1718,7 +1746,7 @@ static int lm90_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct device *dev = &client->dev;
struct i2c_adapter *adapter = to_i2c_adapter(dev->parent);
struct i2c_adapter *adapter = client->adapter;
struct hwmon_channel_info *info;
struct regulator *regulator;
struct device *hwmon_dev;
......@@ -1873,14 +1901,8 @@ static void lm90_alert(struct i2c_client *client, enum i2c_alert_protocol type,
if ((data->flags & LM90_HAVE_BROKEN_ALERT) &&
(alarms & data->alert_alarms)) {
int config;
dev_dbg(&client->dev, "Disabling ALERT#\n");
config = lm90_read_reg(client, LM90_REG_R_CONFIG1);
if (config >= 0)
i2c_smbus_write_byte_data(client,
LM90_REG_W_CONFIG1,
config | 0x80);
lm90_update_confreg(data, data->config | 0x80);
}
} else {
dev_info(&client->dev, "Everything OK\n");
......
This diff is collapsed.
......@@ -4,6 +4,9 @@
*
* Copyright (c) 2015 Kontron
* Author: Vadim V. Vlasov <vvlasov@dev.rtsoft.ru>
*
* Copyright (c) 2019 Advantech
* Author: Amy.Shih <amy.shih@advantech.com.tw>
*/
#include <linux/module.h>
......@@ -50,6 +53,8 @@
#define T_CPU1_HV_REG 0xA0 /* Bank 0; 2 regs (HV/LV) per sensor */
#define PRTS_REG 0x03 /* Bank 2 */
#define PFE_REG 0x00 /* Bank 2; PECI Function Enable */
#define TSI_CTRL_REG 0x50 /* Bank 2; TSI Control Register */
#define FANCTL1_FMR_REG 0x00 /* Bank 3; 1 reg per channel */
#define FANCTL1_OUT_REG 0x10 /* Bank 3; 1 reg per channel */
......@@ -65,6 +70,8 @@ struct nct7904_data {
u32 vsen_mask;
u32 tcpu_mask;
u8 fan_mode[FANCTL_MAX];
u8 enable_dts;
u8 has_dts;
};
/* Access functions */
......@@ -229,11 +236,15 @@ static int nct7904_read_temp(struct device *dev, u32 attr, int channel,
switch (attr) {
case hwmon_temp_input:
if (channel == 0)
if (channel == 4)
ret = nct7904_read_reg16(data, BANK_0, LTD_HV_REG);
else if (channel < 5)
ret = nct7904_read_reg16(data, BANK_0,
TEMP_CH1_HV_REG + channel * 4);
else
ret = nct7904_read_reg16(data, BANK_0,
T_CPU1_HV_REG + (channel - 1) * 2);
T_CPU1_HV_REG + (channel - 5)
* 2);
if (ret < 0)
return ret;
temp = ((ret & 0xff00) >> 5) | (ret & 0x7);
......@@ -249,11 +260,11 @@ static umode_t nct7904_temp_is_visible(const void *_data, u32 attr, int channel)
const struct nct7904_data *data = _data;
if (attr == hwmon_temp_input) {
if (channel == 0) {
if (data->vsen_mask & BIT(17))
if (channel < 5) {
if (data->tcpu_mask & BIT(channel))
return 0444;
} else {
if (data->tcpu_mask & BIT(channel - 1))
if (data->has_dts & BIT(channel - 5))
return 0444;
}
}
......@@ -460,6 +471,7 @@ static int nct7904_probe(struct i2c_client *client,
struct device *dev = &client->dev;
int ret, i;
u32 mask;
u8 val, bit;
data = devm_kzalloc(dev, sizeof(struct nct7904_data), GFP_KERNEL);
if (!data)
......@@ -493,10 +505,65 @@ static int nct7904_probe(struct i2c_client *client,
data->vsen_mask = mask;
/* CPU_TEMP attributes */
ret = nct7904_read_reg16(data, BANK_0, DTS_T_CTRL0_REG);
ret = nct7904_read_reg(data, BANK_0, VT_ADC_CTRL0_REG);
if (ret < 0)
return ret;
if ((ret & 0x6) == 0x6)
data->tcpu_mask |= 1; /* TR1 */
if ((ret & 0x18) == 0x18)
data->tcpu_mask |= 2; /* TR2 */
if ((ret & 0x20) == 0x20)
data->tcpu_mask |= 4; /* TR3 */
if ((ret & 0x80) == 0x80)
data->tcpu_mask |= 8; /* TR4 */
/* LTD */
ret = nct7904_read_reg(data, BANK_0, VT_ADC_CTRL2_REG);
if (ret < 0)
return ret;
if ((ret & 0x02) == 0x02)
data->tcpu_mask |= 0x10;
/* Multi-Function detecting for Volt and TR/TD */
ret = nct7904_read_reg(data, BANK_0, VT_ADC_MD_REG);
if (ret < 0)
return ret;
data->tcpu_mask = ((ret >> 8) & 0xf) | ((ret & 0xf) << 4);
for (i = 0; i < 4; i++) {
val = (ret & (0x03 << i)) >> (i * 2);
bit = (1 << i);
if (val == 0)
data->tcpu_mask &= ~bit;
}
/* PECI */
ret = nct7904_read_reg(data, BANK_2, PFE_REG);
if (ret < 0)
return ret;
if (ret & 0x80) {
data->enable_dts = 1; /* Enable DTS & PECI */
} else {
ret = nct7904_read_reg(data, BANK_2, TSI_CTRL_REG);
if (ret < 0)
return ret;
if (ret & 0x80)
data->enable_dts = 0x3; /* Enable DTS & TSI */
}
/* Check DTS enable status */
if (data->enable_dts) {
ret = nct7904_read_reg(data, BANK_0, DTS_T_CTRL0_REG);
if (ret < 0)
return ret;
data->has_dts = ret & 0xF;
if (data->enable_dts & 0x2) {
ret = nct7904_read_reg(data, BANK_0, DTS_T_CTRL1_REG);
if (ret < 0)
return ret;
data->has_dts |= (ret & 0xF) << 4;
}
}
for (i = 0; i < FANCTL_MAX; i++) {
ret = nct7904_read_reg(data, BANK_3, FANCTL1_FMR_REG + i);
......
......@@ -241,6 +241,12 @@ static ssize_t occ_show_temp_1(struct device *dev,
val = get_unaligned_be16(&temp->sensor_id);
break;
case 1:
/*
* If a sensor reading has expired and couldn't be refreshed,
* OCC returns 0xFFFF for that sensor.
*/
if (temp->value == 0xFFFF)
return -EREMOTEIO;
val = get_unaligned_be16(&temp->value) * 1000;
break;
default:
......
......@@ -64,6 +64,15 @@ config SENSORS_IR38064
This driver can also be built as a module. If so, the module will
be called ir38064.
config SENSORS_IRPS5401
tristate "Infineon IRPS5401"
help
If you say yes here you get hardware monitoring support for the
Infineon IRPS5401 controller.
This driver can also be built as a module. If so, the module will
be called irps5401.
config SENSORS_ISL68137
tristate "Intersil ISL68137"
help
......@@ -154,6 +163,15 @@ config SENSORS_MAX8688
This driver can also be built as a module. If so, the module will
be called max8688.
config SENSORS_PXE1610
tristate "Infineon PXE1610"
help
If you say yes here you get hardware monitoring support for Infineon
PXE1610.
This driver can also be built as a module. If so, the module will
be called pxe1610.
config SENSORS_TPS40422
tristate "TI TPS40422"
help
......
......@@ -9,6 +9,7 @@ obj-$(CONFIG_SENSORS_ADM1275) += adm1275.o
obj-$(CONFIG_SENSORS_IBM_CFFPS) += ibm-cffps.o
obj-$(CONFIG_SENSORS_IR35221) += ir35221.o
obj-$(CONFIG_SENSORS_IR38064) += ir38064.o
obj-$(CONFIG_SENSORS_IRPS5401) += irps5401.o
obj-$(CONFIG_SENSORS_ISL68137) += isl68137.o
obj-$(CONFIG_SENSORS_LM25066) += lm25066.o
obj-$(CONFIG_SENSORS_LTC2978) += ltc2978.o
......@@ -18,6 +19,7 @@ obj-$(CONFIG_SENSORS_MAX20751) += max20751.o
obj-$(CONFIG_SENSORS_MAX31785) += max31785.o
obj-$(CONFIG_SENSORS_MAX34440) += max34440.o
obj-$(CONFIG_SENSORS_MAX8688) += max8688.o
obj-$(CONFIG_SENSORS_PXE1610) += pxe1610.o
obj-$(CONFIG_SENSORS_TPS40422) += tps40422.o
obj-$(CONFIG_SENSORS_TPS53679) += tps53679.o
obj-$(CONFIG_SENSORS_UCD9000) += ucd9000.o
......
......@@ -14,6 +14,8 @@
#include <linux/slab.h>
#include <linux/i2c.h>
#include <linux/bitops.h>
#include <linux/bitfield.h>
#include <linux/log2.h>
#include "pmbus.h"
enum chips { adm1075, adm1272, adm1275, adm1276, adm1278, adm1293, adm1294 };
......@@ -69,6 +71,18 @@ enum chips { adm1075, adm1272, adm1275, adm1276, adm1278, adm1293, adm1294 };
#define ADM1075_VAUX_OV_WARN BIT(7)
#define ADM1075_VAUX_UV_WARN BIT(6)
#define ADM1275_VI_AVG_SHIFT 0
#define ADM1275_VI_AVG_MASK GENMASK(ADM1275_VI_AVG_SHIFT + 2, \
ADM1275_VI_AVG_SHIFT)
#define ADM1275_SAMPLES_AVG_MAX 128
#define ADM1278_PWR_AVG_SHIFT 11
#define ADM1278_PWR_AVG_MASK GENMASK(ADM1278_PWR_AVG_SHIFT + 2, \
ADM1278_PWR_AVG_SHIFT)
#define ADM1278_VI_AVG_SHIFT 8
#define ADM1278_VI_AVG_MASK GENMASK(ADM1278_VI_AVG_SHIFT + 2, \
ADM1278_VI_AVG_SHIFT)
struct adm1275_data {
int id;
bool have_oc_fault;
......@@ -80,6 +94,7 @@ struct adm1275_data {
bool have_pin_min;
bool have_pin_max;
bool have_temp_max;
bool have_power_sampling;
struct pmbus_driver_info info;
};
......@@ -155,6 +170,62 @@ static const struct coefficients adm1293_coefficients[] = {
[18] = { 7658, 0, -3 }, /* power, 21V, irange200 */
};
static int adm1275_read_pmon_config(const struct adm1275_data *data,
struct i2c_client *client, bool is_power)
{
int shift, ret;
u16 mask;
/*
* The PMON configuration register is a 16-bit register only on chips
* supporting power average sampling. On other chips it is an 8-bit
* register.
*/
if (data->have_power_sampling) {
ret = i2c_smbus_read_word_data(client, ADM1275_PMON_CONFIG);
mask = is_power ? ADM1278_PWR_AVG_MASK : ADM1278_VI_AVG_MASK;
shift = is_power ? ADM1278_PWR_AVG_SHIFT : ADM1278_VI_AVG_SHIFT;
} else {
ret = i2c_smbus_read_byte_data(client, ADM1275_PMON_CONFIG);
mask = ADM1275_VI_AVG_MASK;
shift = ADM1275_VI_AVG_SHIFT;
}
if (ret < 0)
return ret;
return (ret & mask) >> shift;
}
static int adm1275_write_pmon_config(const struct adm1275_data *data,
struct i2c_client *client,
bool is_power, u16 word)
{
int shift, ret;
u16 mask;
if (data->have_power_sampling) {
ret = i2c_smbus_read_word_data(client, ADM1275_PMON_CONFIG);
mask = is_power ? ADM1278_PWR_AVG_MASK : ADM1278_VI_AVG_MASK;
shift = is_power ? ADM1278_PWR_AVG_SHIFT : ADM1278_VI_AVG_SHIFT;
} else {
ret = i2c_smbus_read_byte_data(client, ADM1275_PMON_CONFIG);
mask = ADM1275_VI_AVG_MASK;
shift = ADM1275_VI_AVG_SHIFT;
}
if (ret < 0)
return ret;
word = (ret & ~mask) | ((word << shift) & mask);
if (data->have_power_sampling)
ret = i2c_smbus_write_word_data(client, ADM1275_PMON_CONFIG,
word);
else
ret = i2c_smbus_write_byte_data(client, ADM1275_PMON_CONFIG,
word);
return ret;
}
static int adm1275_read_word_data(struct i2c_client *client, int page, int reg)
{
const struct pmbus_driver_info *info = pmbus_get_driver_info(client);
......@@ -233,6 +304,21 @@ static int adm1275_read_word_data(struct i2c_client *client, int page, int reg)
if (!data->have_temp_max)
return -ENXIO;
break;
case PMBUS_VIRT_POWER_SAMPLES:
if (!data->have_power_sampling)
return -ENXIO;
ret = adm1275_read_pmon_config(data, client, true);
if (ret < 0)
break;
ret = BIT(ret);
break;
case PMBUS_VIRT_IN_SAMPLES:
case PMBUS_VIRT_CURR_SAMPLES:
ret = adm1275_read_pmon_config(data, client, false);
if (ret < 0)
break;
ret = BIT(ret);
break;
default:
ret = -ENODATA;
break;
......@@ -277,6 +363,19 @@ static int adm1275_write_word_data(struct i2c_client *client, int page, int reg,
case PMBUS_VIRT_RESET_TEMP_HISTORY:
ret = pmbus_write_word_data(client, 0, ADM1278_PEAK_TEMP, 0);
break;
case PMBUS_VIRT_POWER_SAMPLES:
if (!data->have_power_sampling)
return -ENXIO;
word = clamp_val(word, 1, ADM1275_SAMPLES_AVG_MAX);
ret = adm1275_write_pmon_config(data, client, true,
ilog2(word));
break;
case PMBUS_VIRT_IN_SAMPLES:
case PMBUS_VIRT_CURR_SAMPLES:
word = clamp_val(word, 1, ADM1275_SAMPLES_AVG_MAX);
ret = adm1275_write_pmon_config(data, client, false,
ilog2(word));
break;
default:
ret = -ENODATA;
break;
......@@ -430,7 +529,8 @@ static int adm1275_probe(struct i2c_client *client,
info->format[PSC_CURRENT_OUT] = direct;
info->format[PSC_POWER] = direct;
info->format[PSC_TEMPERATURE] = direct;
info->func[0] = PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT;
info->func[0] = PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT |
PMBUS_HAVE_SAMPLES;
info->read_word_data = adm1275_read_word_data;
info->read_byte_data = adm1275_read_byte_data;
......@@ -471,6 +571,7 @@ static int adm1275_probe(struct i2c_client *client,
data->have_vout = true;
data->have_pin_max = true;
data->have_temp_max = true;
data->have_power_sampling = true;
coefficients = adm1272_coefficients;
vindex = (config & ADM1275_VRANGE) ? 1 : 0;
......@@ -556,6 +657,7 @@ static int adm1275_probe(struct i2c_client *client,
data->have_vout = true;
data->have_pin_max = true;
data->have_temp_max = true;
data->have_power_sampling = true;
coefficients = adm1278_coefficients;
vindex = 0;
......@@ -591,6 +693,7 @@ static int adm1275_probe(struct i2c_client *client,
data->have_pin_min = true;
data->have_pin_max = true;
data->have_mfr_vaux_status = true;
data->have_power_sampling = true;
coefficients = adm1293_coefficients;
......
// SPDX-License-Identifier: GPL-2.0+
/*
* Hardware monitoring driver for the Infineon IRPS5401M PMIC.
*
* Copyright (c) 2019 SED Systems, a division of Calian Ltd.
*
* The device supports VOUT_PEAK, IOUT_PEAK, and TEMPERATURE_PEAK, however
* this driver does not currently support them.
*/
#include <linux/err.h>
#include <linux/i2c.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include "pmbus.h"
#define IRPS5401_SW_FUNC (PMBUS_HAVE_VIN | PMBUS_HAVE_IIN | \
PMBUS_HAVE_STATUS_INPUT | \
PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT | \
PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT | \
PMBUS_HAVE_PIN | PMBUS_HAVE_POUT | \
PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP)
#define IRPS5401_LDO_FUNC (PMBUS_HAVE_VIN | \
PMBUS_HAVE_STATUS_INPUT | \
PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT | \
PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT | \
PMBUS_HAVE_PIN | PMBUS_HAVE_POUT | \
PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP)
static struct pmbus_driver_info irps5401_info = {
.pages = 5,
.func[0] = IRPS5401_SW_FUNC,
.func[1] = IRPS5401_SW_FUNC,
.func[2] = IRPS5401_SW_FUNC,
.func[3] = IRPS5401_SW_FUNC,
.func[4] = IRPS5401_LDO_FUNC,
};
static int irps5401_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
return pmbus_do_probe(client, id, &irps5401_info);
}
static const struct i2c_device_id irps5401_id[] = {
{"irps5401", 0},
{}
};
MODULE_DEVICE_TABLE(i2c, irps5401_id);
static struct i2c_driver irps5401_driver = {
.driver = {
.name = "irps5401",
},
.probe = irps5401_probe,
.remove = pmbus_do_remove,
.id_table = irps5401_id,
};
module_i2c_driver(irps5401_driver);
MODULE_AUTHOR("Robert Hancock");
MODULE_DESCRIPTION("PMBus driver for Infineon IRPS5401");
MODULE_LICENSE("GPL");
// SPDX-License-Identifier: GPL-2.0+
/*
* Hardware monitoring driver for Infineon PXE1610
*
* Copyright (c) 2019 Facebook Inc
*
*/
#include <linux/err.h>
#include <linux/i2c.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include "pmbus.h"
#define PXE1610_NUM_PAGES 3
/* Identify chip parameters. */
static int pxe1610_identify(struct i2c_client *client,
struct pmbus_driver_info *info)
{
if (pmbus_check_byte_register(client, 0, PMBUS_VOUT_MODE)) {
u8 vout_mode;
int ret;
/* Read the register with VOUT scaling value.*/
ret = pmbus_read_byte_data(client, 0, PMBUS_VOUT_MODE);
if (ret < 0)
return ret;
vout_mode = ret & GENMASK(4, 0);
switch (vout_mode) {
case 1:
info->vrm_version = vr12;
break;
case 2:
info->vrm_version = vr13;
break;
default:
return -ENODEV;
}
}
return 0;
}
static struct pmbus_driver_info pxe1610_info = {
.pages = PXE1610_NUM_PAGES,
.format[PSC_VOLTAGE_IN] = linear,
.format[PSC_VOLTAGE_OUT] = vid,
.format[PSC_CURRENT_IN] = linear,
.format[PSC_CURRENT_OUT] = linear,
.format[PSC_TEMPERATURE] = linear,
.format[PSC_POWER] = linear,
.func[0] = PMBUS_HAVE_VIN
| PMBUS_HAVE_VOUT | PMBUS_HAVE_IIN
| PMBUS_HAVE_IOUT | PMBUS_HAVE_PIN
| PMBUS_HAVE_POUT | PMBUS_HAVE_TEMP
| PMBUS_HAVE_STATUS_VOUT | PMBUS_HAVE_STATUS_IOUT
| PMBUS_HAVE_STATUS_INPUT | PMBUS_HAVE_STATUS_TEMP,
.func[1] = PMBUS_HAVE_VIN
| PMBUS_HAVE_VOUT | PMBUS_HAVE_IIN
| PMBUS_HAVE_IOUT | PMBUS_HAVE_PIN
| PMBUS_HAVE_POUT | PMBUS_HAVE_TEMP
| PMBUS_HAVE_STATUS_VOUT | PMBUS_HAVE_STATUS_IOUT
| PMBUS_HAVE_STATUS_INPUT | PMBUS_HAVE_STATUS_TEMP,
.func[2] = PMBUS_HAVE_VIN
| PMBUS_HAVE_VOUT | PMBUS_HAVE_IIN
| PMBUS_HAVE_IOUT | PMBUS_HAVE_PIN
| PMBUS_HAVE_POUT | PMBUS_HAVE_TEMP
| PMBUS_HAVE_STATUS_VOUT | PMBUS_HAVE_STATUS_IOUT
| PMBUS_HAVE_STATUS_INPUT | PMBUS_HAVE_STATUS_TEMP,
.identify = pxe1610_identify,
};
static int pxe1610_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct pmbus_driver_info *info;
u8 buf[I2C_SMBUS_BLOCK_MAX];
int ret;
if (!i2c_check_functionality(
client->adapter,
I2C_FUNC_SMBUS_READ_BYTE_DATA
| I2C_FUNC_SMBUS_READ_WORD_DATA
| I2C_FUNC_SMBUS_READ_BLOCK_DATA))
return -ENODEV;
/*
* By default this device doesn't boot to page 0, so set page 0
* to access all pmbus registers.
*/
i2c_smbus_write_byte_data(client, PMBUS_PAGE, 0);
/* Read Manufacturer id */
ret = i2c_smbus_read_block_data(client, PMBUS_MFR_ID, buf);
if (ret < 0) {
dev_err(&client->dev, "Failed to read PMBUS_MFR_ID\n");
return ret;
}
if (ret != 2 || strncmp(buf, "XP", 2)) {
dev_err(&client->dev, "MFR_ID unrecognized\n");
return -ENODEV;
}
info = devm_kmemdup(&client->dev, &pxe1610_info,
sizeof(struct pmbus_driver_info),
GFP_KERNEL);
if (!info)
return -ENOMEM;
return pmbus_do_probe(client, id, info);
}
static const struct i2c_device_id pxe1610_id[] = {
{"pxe1610", 0},
{"pxe1110", 0},
{"pxm1310", 0},
{}
};
MODULE_DEVICE_TABLE(i2c, pxe1610_id);
static struct i2c_driver pxe1610_driver = {
.driver = {
.name = "pxe1610",
},
.probe = pxe1610_probe,
.remove = pmbus_do_remove,
.id_table = pxe1610_id,
};
module_i2c_driver(pxe1610_driver);
MODULE_AUTHOR("Vijay Khemka <vijaykhemka@fb.com>");
MODULE_DESCRIPTION("PMBus driver for Infineon PXE1610, PXE1110 and PXM1310");
MODULE_LICENSE("GPL");
......@@ -320,8 +320,10 @@ static int pwm_fan_probe(struct platform_device *pdev)
dev_err(dev, "Failed to enable fan supply: %d\n", ret);
return ret;
}
devm_add_action_or_reset(dev, pwm_fan_regulator_disable,
ctx->reg_en);
ret = devm_add_action_or_reset(dev, pwm_fan_regulator_disable,
ctx->reg_en);
if (ret)
return ret;
}
ctx->pwm_value = MAX_PWM;
......@@ -337,7 +339,9 @@ static int pwm_fan_probe(struct platform_device *pdev)
return ret;
}
timer_setup(&ctx->rpm_timer, sample_timer, 0);
devm_add_action_or_reset(dev, pwm_fan_pwm_disable, ctx);
ret = devm_add_action_or_reset(dev, pwm_fan_pwm_disable, ctx);
if (ret)
return ret;
of_property_read_u32(dev->of_node, "pulses-per-revolution", &ppr);
ctx->pulses_per_revolution = ppr;
......
// SPDX-License-Identifier: GPL-2.0
/*
* System Control and Power Interface(SCPI) based hwmon sensor driver
*
* Copyright (C) 2015 ARM Ltd.
* Punit Agrawal <punit.agrawal@arm.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program is distributed "as is" WITHOUT ANY WARRANTY of any
* kind, whether express or implied; without even the implied warranty
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <linux/hwmon.h>
......
......@@ -351,6 +351,8 @@ static ssize_t fan_div_store(struct device *dev,
tmp |= data->fan_div[2] << 4;
smsc47m1_write_value(data, SMSC47M2_REG_FANDIV3, tmp);
break;
default:
BUG();
}
/* Preserve fan min */
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment