mirror of
https://github.com/torvalds/linux.git
synced 2024-11-21 19:41:42 +00:00
hwmon updates for v5.3
New drivers for Infineon PXE1610 and IRPS5401 Minor improvements, cleanup, and fixes in several drivers -----BEGIN PGP SIGNATURE----- Version: GnuPG v1 iQIcBAABAgAGBQJdJNpEAAoJEMsfJm/On5mBjlkP/1DEAEtL6pJYyl0zmyR3QrUN CAkSvvH46ZFGLbZb/ZC3XuXTaBbxFmZxeZP0NLSoOh45zmNoo0IjPzmSBHpm9R+7 ENZBLlMhtbR3M7zJO5kEb5kgOpnzf46KqEfOi72J51lZx+aabepSfkvzV1Zqf6TY PjuXwYG+VhhBobpwVNe2qixoQcHxhKbhheYvwbvfVjCA8YFqa08MkzbXjqHGcGwP mNKko/okRlLf1Qqq4rONlfixfO6rorKKN1oiDihFFRrNmJoT5n92jGaB+RLx2sHY pC8iHrOCnbe8iXXKYg8cwEVZ8OKUDfOSGL3RX2yVk6ZF7B13QZjqX7t437h3hAyD i0rWUyolKxwapAr2yUgO8QhHun6Zx/oOzpBPdGIdy0yhsbas9f9e5unGm9rSEWHh 2aIMUED5YKtXqdujos05AYaw9GgQVPEbr16xkjU8DGc/qtWrWUFeER0dJDNDnduO yLl9yDwwq0bSoSEg54bA6ib4CPHyKtD8ZNrYDfGiTMO9xZqrwWssFMguvYnWbuNZ cAh6lEQODL5bhg4vGIMfqt3Ub7EoTzjCCOnQRrOU4ERbxazaB56+A2VyYv+7LwLd sPfxldSMjyUYrnyDk+WQkTgRl0WsV/Sr4FS6Z4NOjZWyGoTCsjB8Z/m/5eEydIas VbeuAQEKoxvvJOWaRu00 =G/oG -----END PGP SIGNATURE----- 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 ...
This commit is contained in:
commit
64b08df460
90
Documentation/hwmon/pxe1610
Normal file
90
Documentation/hwmon/pxe1610
Normal file
@ -0,0 +1,90 @@
|
||||
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");
|
||||
|
@ -92,7 +92,8 @@ module_param(clock, int, 0444);
|
||||
#define FAN_RPM_MIN 240
|
||||
#define FAN_RPM_MAX 30000
|
||||
|
||||
#define DIV_FROM_REG(reg) (1 << (reg & 7))
|
||||
#define DIV_FROM_REG(reg) (1 << ((reg) & 7))
|
||||
#define DAC_LIMIT(v12) ((v12) ? 180 : 76)
|
||||
|
||||
/*
|
||||
* Client data (each client gets its own)
|
||||
@ -100,11 +101,9 @@ module_param(clock, int, 0444);
|
||||
|
||||
struct max6650_data {
|
||||
struct i2c_client *client;
|
||||
const struct attribute_group *groups[3];
|
||||
struct thermal_cooling_device *cooling_dev;
|
||||
struct mutex update_lock;
|
||||
struct mutex update_lock; /* protect alarm register updates */
|
||||
int nr_fans;
|
||||
char valid; /* zero until following fields are valid */
|
||||
bool valid; /* false until following fields are valid */
|
||||
unsigned long last_updated; /* in jiffies */
|
||||
|
||||
/* register values */
|
||||
@ -114,6 +113,7 @@ struct max6650_data {
|
||||
u8 count;
|
||||
u8 dac;
|
||||
u8 alarm;
|
||||
u8 alarm_en;
|
||||
unsigned long cooling_dev_state;
|
||||
};
|
||||
|
||||
@ -137,41 +137,60 @@ static const struct of_device_id __maybe_unused max6650_dt_match[] = {
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, max6650_dt_match);
|
||||
|
||||
static int dac_to_pwm(int dac, bool v12)
|
||||
{
|
||||
/*
|
||||
* Useful range for dac is 0-180 for 12V fans and 0-76 for 5V fans.
|
||||
* Lower DAC values mean higher speeds.
|
||||
*/
|
||||
return clamp_val(255 - (255 * dac) / DAC_LIMIT(v12), 0, 255);
|
||||
}
|
||||
|
||||
static u8 pwm_to_dac(unsigned int pwm, bool v12)
|
||||
{
|
||||
int limit = DAC_LIMIT(v12);
|
||||
|
||||
return limit - (limit * pwm) / 255;
|
||||
}
|
||||
|
||||
static struct max6650_data *max6650_update_device(struct device *dev)
|
||||
{
|
||||
struct max6650_data *data = dev_get_drvdata(dev);
|
||||
struct i2c_client *client = data->client;
|
||||
int reg, err = 0;
|
||||
int i;
|
||||
|
||||
mutex_lock(&data->update_lock);
|
||||
|
||||
if (time_after(jiffies, data->last_updated + HZ) || !data->valid) {
|
||||
data->speed = i2c_smbus_read_byte_data(client,
|
||||
MAX6650_REG_SPEED);
|
||||
data->config = i2c_smbus_read_byte_data(client,
|
||||
MAX6650_REG_CONFIG);
|
||||
for (i = 0; i < data->nr_fans; i++) {
|
||||
data->tach[i] = i2c_smbus_read_byte_data(client,
|
||||
tach_reg[i]);
|
||||
reg = i2c_smbus_read_byte_data(client, tach_reg[i]);
|
||||
if (reg < 0) {
|
||||
err = reg;
|
||||
goto error;
|
||||
}
|
||||
data->tach[i] = reg;
|
||||
}
|
||||
data->count = i2c_smbus_read_byte_data(client,
|
||||
MAX6650_REG_COUNT);
|
||||
data->dac = i2c_smbus_read_byte_data(client, MAX6650_REG_DAC);
|
||||
|
||||
/*
|
||||
* Alarms are cleared on read in case the condition that
|
||||
* caused the alarm is removed. Keep the value latched here
|
||||
* for providing the register through different alarm files.
|
||||
*/
|
||||
data->alarm |= i2c_smbus_read_byte_data(client,
|
||||
MAX6650_REG_ALARM);
|
||||
|
||||
reg = i2c_smbus_read_byte_data(client, MAX6650_REG_ALARM);
|
||||
if (reg < 0) {
|
||||
err = reg;
|
||||
goto error;
|
||||
}
|
||||
data->alarm |= reg;
|
||||
data->last_updated = jiffies;
|
||||
data->valid = 1;
|
||||
data->valid = true;
|
||||
}
|
||||
|
||||
error:
|
||||
mutex_unlock(&data->update_lock);
|
||||
|
||||
if (err)
|
||||
data = ERR_PTR(err);
|
||||
return data;
|
||||
}
|
||||
|
||||
@ -199,26 +218,6 @@ static int max6650_set_operating_mode(struct max6650_data *data, u8 mode)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static ssize_t fan_show(struct device *dev, struct device_attribute *devattr,
|
||||
char *buf)
|
||||
{
|
||||
struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
|
||||
struct max6650_data *data = max6650_update_device(dev);
|
||||
int rpm;
|
||||
|
||||
/*
|
||||
* Calculation details:
|
||||
*
|
||||
* Each tachometer counts over an interval given by the "count"
|
||||
* register (0.25, 0.5, 1 or 2 seconds). This module assumes
|
||||
* that the fans produce two pulses per revolution (this seems
|
||||
* to be the most common).
|
||||
*/
|
||||
|
||||
rpm = ((data->tach[attr->index] * 120) / DIV_FROM_REG(data->count));
|
||||
return sprintf(buf, "%d\n", rpm);
|
||||
}
|
||||
|
||||
/*
|
||||
* Set the fan speed to the specified RPM (or read back the RPM setting).
|
||||
* This works in closed loop mode only. Use pwm1 for open loop speed setting.
|
||||
@ -260,26 +259,6 @@ static ssize_t fan_show(struct device *dev, struct device_attribute *devattr,
|
||||
* controlled.
|
||||
*/
|
||||
|
||||
static ssize_t fan1_target_show(struct device *dev,
|
||||
struct device_attribute *devattr, char *buf)
|
||||
{
|
||||
struct max6650_data *data = max6650_update_device(dev);
|
||||
int kscale, ktach, rpm;
|
||||
|
||||
/*
|
||||
* Use the datasheet equation:
|
||||
*
|
||||
* FanSpeed = KSCALE x fCLK / [256 x (KTACH + 1)]
|
||||
*
|
||||
* then multiply by 60 to give rpm.
|
||||
*/
|
||||
|
||||
kscale = DIV_FROM_REG(data->config);
|
||||
ktach = data->speed;
|
||||
rpm = 60 * kscale * clock / (256 * (ktach + 1));
|
||||
return sprintf(buf, "%d\n", rpm);
|
||||
}
|
||||
|
||||
static int max6650_set_target(struct max6650_data *data, unsigned long rpm)
|
||||
{
|
||||
int kscale, ktach;
|
||||
@ -308,197 +287,8 @@ static int max6650_set_target(struct max6650_data *data, unsigned long rpm)
|
||||
data->speed);
|
||||
}
|
||||
|
||||
static ssize_t fan1_target_store(struct device *dev,
|
||||
struct device_attribute *devattr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
struct max6650_data *data = dev_get_drvdata(dev);
|
||||
unsigned long rpm;
|
||||
int err;
|
||||
|
||||
err = kstrtoul(buf, 10, &rpm);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
mutex_lock(&data->update_lock);
|
||||
|
||||
err = max6650_set_target(data, rpm);
|
||||
|
||||
mutex_unlock(&data->update_lock);
|
||||
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
/*
|
||||
* Get/set the fan speed in open loop mode using pwm1 sysfs file.
|
||||
* Speed is given as a relative value from 0 to 255, where 255 is maximum
|
||||
* speed. Note that this is done by writing directly to the chip's DAC,
|
||||
* it won't change the closed loop speed set by fan1_target.
|
||||
* Also note that due to rounding errors it is possible that you don't read
|
||||
* back exactly the value you have set.
|
||||
*/
|
||||
|
||||
static ssize_t pwm1_show(struct device *dev, struct device_attribute *devattr,
|
||||
char *buf)
|
||||
{
|
||||
int pwm;
|
||||
struct max6650_data *data = max6650_update_device(dev);
|
||||
|
||||
/*
|
||||
* Useful range for dac is 0-180 for 12V fans and 0-76 for 5V fans.
|
||||
* Lower DAC values mean higher speeds.
|
||||
*/
|
||||
if (data->config & MAX6650_CFG_V12)
|
||||
pwm = 255 - (255 * (int)data->dac)/180;
|
||||
else
|
||||
pwm = 255 - (255 * (int)data->dac)/76;
|
||||
|
||||
if (pwm < 0)
|
||||
pwm = 0;
|
||||
|
||||
return sprintf(buf, "%d\n", pwm);
|
||||
}
|
||||
|
||||
static ssize_t pwm1_store(struct device *dev,
|
||||
struct device_attribute *devattr, const char *buf,
|
||||
size_t count)
|
||||
{
|
||||
struct max6650_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;
|
||||
|
||||
pwm = clamp_val(pwm, 0, 255);
|
||||
|
||||
mutex_lock(&data->update_lock);
|
||||
|
||||
if (data->config & MAX6650_CFG_V12)
|
||||
data->dac = 180 - (180 * pwm)/255;
|
||||
else
|
||||
data->dac = 76 - (76 * pwm)/255;
|
||||
err = i2c_smbus_write_byte_data(client, MAX6650_REG_DAC, data->dac);
|
||||
|
||||
mutex_unlock(&data->update_lock);
|
||||
|
||||
return err < 0 ? err : count;
|
||||
}
|
||||
|
||||
/*
|
||||
* Get/Set controller mode:
|
||||
* Possible values:
|
||||
* 0 = Fan always on
|
||||
* 1 = Open loop, Voltage is set according to speed, not regulated.
|
||||
* 2 = Closed loop, RPM for all fans regulated by fan1 tachometer
|
||||
* 3 = Fan off
|
||||
*/
|
||||
static ssize_t pwm1_enable_show(struct device *dev,
|
||||
struct device_attribute *devattr, char *buf)
|
||||
{
|
||||
struct max6650_data *data = max6650_update_device(dev);
|
||||
int mode = (data->config & MAX6650_CFG_MODE_MASK) >> 4;
|
||||
int sysfs_modes[4] = {0, 3, 2, 1};
|
||||
|
||||
return sprintf(buf, "%d\n", sysfs_modes[mode]);
|
||||
}
|
||||
|
||||
static ssize_t pwm1_enable_store(struct device *dev,
|
||||
struct device_attribute *devattr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
struct max6650_data *data = dev_get_drvdata(dev);
|
||||
unsigned long mode;
|
||||
int err;
|
||||
const u8 max6650_modes[] = {
|
||||
MAX6650_CFG_MODE_ON,
|
||||
MAX6650_CFG_MODE_OPEN_LOOP,
|
||||
MAX6650_CFG_MODE_CLOSED_LOOP,
|
||||
MAX6650_CFG_MODE_OFF,
|
||||
};
|
||||
|
||||
err = kstrtoul(buf, 10, &mode);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
if (mode >= ARRAY_SIZE(max6650_modes))
|
||||
return -EINVAL;
|
||||
|
||||
mutex_lock(&data->update_lock);
|
||||
|
||||
max6650_set_operating_mode(data, max6650_modes[mode]);
|
||||
|
||||
mutex_unlock(&data->update_lock);
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
/*
|
||||
* Read/write functions for fan1_div sysfs file. The MAX6650 has no such
|
||||
* divider. We handle this by converting between divider and counttime:
|
||||
*
|
||||
* (counttime == k) <==> (divider == 2^k), k = 0, 1, 2, or 3
|
||||
*
|
||||
* Lower values of k allow to connect a faster fan without the risk of
|
||||
* counter overflow. The price is lower resolution. You can also set counttime
|
||||
* using the module parameter. Note that the module parameter "prescaler" also
|
||||
* influences the behaviour. Unfortunately, there's no sysfs attribute
|
||||
* defined for that. See the data sheet for details.
|
||||
*/
|
||||
|
||||
static ssize_t fan1_div_show(struct device *dev,
|
||||
struct device_attribute *devattr, char *buf)
|
||||
{
|
||||
struct max6650_data *data = max6650_update_device(dev);
|
||||
|
||||
return sprintf(buf, "%d\n", DIV_FROM_REG(data->count));
|
||||
}
|
||||
|
||||
static ssize_t fan1_div_store(struct device *dev,
|
||||
struct device_attribute *devattr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
struct max6650_data *data = dev_get_drvdata(dev);
|
||||
struct i2c_client *client = data->client;
|
||||
unsigned long div;
|
||||
int err;
|
||||
|
||||
err = kstrtoul(buf, 10, &div);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
mutex_lock(&data->update_lock);
|
||||
switch (div) {
|
||||
case 1:
|
||||
data->count = 0;
|
||||
break;
|
||||
case 2:
|
||||
data->count = 1;
|
||||
break;
|
||||
case 4:
|
||||
data->count = 2;
|
||||
break;
|
||||
case 8:
|
||||
data->count = 3;
|
||||
break;
|
||||
default:
|
||||
mutex_unlock(&data->update_lock);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
i2c_smbus_write_byte_data(client, MAX6650_REG_COUNT, data->count);
|
||||
mutex_unlock(&data->update_lock);
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
/*
|
||||
* Get alarm stati:
|
||||
* Get gpio alarm status:
|
||||
* Possible values:
|
||||
* 0 = no alarm
|
||||
* 1 = alarm
|
||||
@ -509,42 +299,30 @@ static ssize_t alarm_show(struct device *dev,
|
||||
{
|
||||
struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
|
||||
struct max6650_data *data = max6650_update_device(dev);
|
||||
struct i2c_client *client = data->client;
|
||||
int alarm = 0;
|
||||
bool alarm;
|
||||
|
||||
if (data->alarm & attr->index) {
|
||||
if (IS_ERR(data))
|
||||
return PTR_ERR(data);
|
||||
|
||||
alarm = data->alarm & attr->index;
|
||||
if (alarm) {
|
||||
mutex_lock(&data->update_lock);
|
||||
alarm = 1;
|
||||
data->alarm &= ~attr->index;
|
||||
data->alarm |= i2c_smbus_read_byte_data(client,
|
||||
MAX6650_REG_ALARM);
|
||||
data->valid = false;
|
||||
mutex_unlock(&data->update_lock);
|
||||
}
|
||||
|
||||
return sprintf(buf, "%d\n", alarm);
|
||||
}
|
||||
|
||||
static SENSOR_DEVICE_ATTR_RO(fan1_input, fan, 0);
|
||||
static SENSOR_DEVICE_ATTR_RO(fan2_input, fan, 1);
|
||||
static SENSOR_DEVICE_ATTR_RO(fan3_input, fan, 2);
|
||||
static SENSOR_DEVICE_ATTR_RO(fan4_input, fan, 3);
|
||||
static DEVICE_ATTR_RW(fan1_target);
|
||||
static DEVICE_ATTR_RW(fan1_div);
|
||||
static DEVICE_ATTR_RW(pwm1_enable);
|
||||
static DEVICE_ATTR_RW(pwm1);
|
||||
static SENSOR_DEVICE_ATTR_RO(fan1_max_alarm, alarm, MAX6650_ALRM_MAX);
|
||||
static SENSOR_DEVICE_ATTR_RO(fan1_min_alarm, alarm, MAX6650_ALRM_MIN);
|
||||
static SENSOR_DEVICE_ATTR_RO(fan1_fault, alarm, MAX6650_ALRM_TACH);
|
||||
static SENSOR_DEVICE_ATTR_RO(gpio1_alarm, alarm, MAX6650_ALRM_GPIO1);
|
||||
static SENSOR_DEVICE_ATTR_RO(gpio2_alarm, alarm, MAX6650_ALRM_GPIO2);
|
||||
|
||||
static umode_t max6650_attrs_visible(struct kobject *kobj, struct attribute *a,
|
||||
int n)
|
||||
int n)
|
||||
{
|
||||
struct device *dev = container_of(kobj, struct device, kobj);
|
||||
struct max6650_data *data = dev_get_drvdata(dev);
|
||||
struct i2c_client *client = data->client;
|
||||
u8 alarm_en = i2c_smbus_read_byte_data(client, MAX6650_REG_ALARM_EN);
|
||||
struct device_attribute *devattr;
|
||||
|
||||
/*
|
||||
@ -552,12 +330,9 @@ static umode_t max6650_attrs_visible(struct kobject *kobj, struct attribute *a,
|
||||
*/
|
||||
|
||||
devattr = container_of(a, struct device_attribute, attr);
|
||||
if (devattr == &sensor_dev_attr_fan1_max_alarm.dev_attr
|
||||
|| devattr == &sensor_dev_attr_fan1_min_alarm.dev_attr
|
||||
|| devattr == &sensor_dev_attr_fan1_fault.dev_attr
|
||||
|| devattr == &sensor_dev_attr_gpio1_alarm.dev_attr
|
||||
|| devattr == &sensor_dev_attr_gpio2_alarm.dev_attr) {
|
||||
if (!(alarm_en & to_sensor_dev_attr(devattr)->index))
|
||||
if (devattr == &sensor_dev_attr_gpio1_alarm.dev_attr ||
|
||||
devattr == &sensor_dev_attr_gpio2_alarm.dev_attr) {
|
||||
if (!(data->alarm_en & to_sensor_dev_attr(devattr)->index))
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -565,14 +340,6 @@ static umode_t max6650_attrs_visible(struct kobject *kobj, struct attribute *a,
|
||||
}
|
||||
|
||||
static struct attribute *max6650_attrs[] = {
|
||||
&sensor_dev_attr_fan1_input.dev_attr.attr,
|
||||
&dev_attr_fan1_target.attr,
|
||||
&dev_attr_fan1_div.attr,
|
||||
&dev_attr_pwm1_enable.attr,
|
||||
&dev_attr_pwm1.attr,
|
||||
&sensor_dev_attr_fan1_max_alarm.dev_attr.attr,
|
||||
&sensor_dev_attr_fan1_min_alarm.dev_attr.attr,
|
||||
&sensor_dev_attr_fan1_fault.dev_attr.attr,
|
||||
&sensor_dev_attr_gpio1_alarm.dev_attr.attr,
|
||||
&sensor_dev_attr_gpio2_alarm.dev_attr.attr,
|
||||
NULL
|
||||
@ -583,27 +350,17 @@ static const struct attribute_group max6650_group = {
|
||||
.is_visible = max6650_attrs_visible,
|
||||
};
|
||||
|
||||
static struct attribute *max6651_attrs[] = {
|
||||
&sensor_dev_attr_fan2_input.dev_attr.attr,
|
||||
&sensor_dev_attr_fan3_input.dev_attr.attr,
|
||||
&sensor_dev_attr_fan4_input.dev_attr.attr,
|
||||
static const struct attribute_group *max6650_groups[] = {
|
||||
&max6650_group,
|
||||
NULL
|
||||
};
|
||||
|
||||
static const struct attribute_group max6651_group = {
|
||||
.attrs = max6651_attrs,
|
||||
};
|
||||
|
||||
/*
|
||||
* Real code
|
||||
*/
|
||||
|
||||
static int max6650_init_client(struct max6650_data *data,
|
||||
struct i2c_client *client)
|
||||
{
|
||||
struct device *dev = &client->dev;
|
||||
int config;
|
||||
int err = -EIO;
|
||||
int reg;
|
||||
int err;
|
||||
u32 voltage;
|
||||
u32 prescale;
|
||||
u32 target_rpm;
|
||||
@ -617,21 +374,20 @@ static int max6650_init_client(struct max6650_data *data,
|
||||
&prescale))
|
||||
prescale = prescaler;
|
||||
|
||||
config = i2c_smbus_read_byte_data(client, MAX6650_REG_CONFIG);
|
||||
|
||||
if (config < 0) {
|
||||
dev_err(dev, "Error reading config, aborting.\n");
|
||||
return err;
|
||||
reg = i2c_smbus_read_byte_data(client, MAX6650_REG_CONFIG);
|
||||
if (reg < 0) {
|
||||
dev_err(dev, "Error reading config register, aborting.\n");
|
||||
return reg;
|
||||
}
|
||||
|
||||
switch (voltage) {
|
||||
case 0:
|
||||
break;
|
||||
case 5:
|
||||
config &= ~MAX6650_CFG_V12;
|
||||
reg &= ~MAX6650_CFG_V12;
|
||||
break;
|
||||
case 12:
|
||||
config |= MAX6650_CFG_V12;
|
||||
reg |= MAX6650_CFG_V12;
|
||||
break;
|
||||
default:
|
||||
dev_err(dev, "illegal value for fan_voltage (%d)\n", voltage);
|
||||
@ -641,22 +397,22 @@ static int max6650_init_client(struct max6650_data *data,
|
||||
case 0:
|
||||
break;
|
||||
case 1:
|
||||
config &= ~MAX6650_CFG_PRESCALER_MASK;
|
||||
reg &= ~MAX6650_CFG_PRESCALER_MASK;
|
||||
break;
|
||||
case 2:
|
||||
config = (config & ~MAX6650_CFG_PRESCALER_MASK)
|
||||
reg = (reg & ~MAX6650_CFG_PRESCALER_MASK)
|
||||
| MAX6650_CFG_PRESCALER_2;
|
||||
break;
|
||||
case 4:
|
||||
config = (config & ~MAX6650_CFG_PRESCALER_MASK)
|
||||
reg = (reg & ~MAX6650_CFG_PRESCALER_MASK)
|
||||
| MAX6650_CFG_PRESCALER_4;
|
||||
break;
|
||||
case 8:
|
||||
config = (config & ~MAX6650_CFG_PRESCALER_MASK)
|
||||
reg = (reg & ~MAX6650_CFG_PRESCALER_MASK)
|
||||
| MAX6650_CFG_PRESCALER_8;
|
||||
break;
|
||||
case 16:
|
||||
config = (config & ~MAX6650_CFG_PRESCALER_MASK)
|
||||
reg = (reg & ~MAX6650_CFG_PRESCALER_MASK)
|
||||
| MAX6650_CFG_PRESCALER_16;
|
||||
break;
|
||||
default:
|
||||
@ -664,16 +420,43 @@ static int max6650_init_client(struct max6650_data *data,
|
||||
}
|
||||
|
||||
dev_info(dev, "Fan voltage: %dV, prescaler: %d.\n",
|
||||
(config & MAX6650_CFG_V12) ? 12 : 5,
|
||||
1 << (config & MAX6650_CFG_PRESCALER_MASK));
|
||||
(reg & MAX6650_CFG_V12) ? 12 : 5,
|
||||
1 << (reg & MAX6650_CFG_PRESCALER_MASK));
|
||||
|
||||
if (i2c_smbus_write_byte_data(client, MAX6650_REG_CONFIG, config)) {
|
||||
err = i2c_smbus_write_byte_data(client, MAX6650_REG_CONFIG, reg);
|
||||
if (err) {
|
||||
dev_err(dev, "Config write error, aborting.\n");
|
||||
return err;
|
||||
}
|
||||
data->config = reg;
|
||||
|
||||
data->config = config;
|
||||
data->count = i2c_smbus_read_byte_data(client, MAX6650_REG_COUNT);
|
||||
reg = i2c_smbus_read_byte_data(client, MAX6650_REG_SPEED);
|
||||
if (reg < 0) {
|
||||
dev_err(dev, "Failed to read speed register, aborting.\n");
|
||||
return reg;
|
||||
}
|
||||
data->speed = reg;
|
||||
|
||||
reg = i2c_smbus_read_byte_data(client, MAX6650_REG_DAC);
|
||||
if (reg < 0) {
|
||||
dev_err(dev, "Failed to read DAC register, aborting.\n");
|
||||
return reg;
|
||||
}
|
||||
data->dac = reg;
|
||||
|
||||
reg = i2c_smbus_read_byte_data(client, MAX6650_REG_COUNT);
|
||||
if (reg < 0) {
|
||||
dev_err(dev, "Failed to read count register, aborting.\n");
|
||||
return reg;
|
||||
}
|
||||
data->count = reg;
|
||||
|
||||
reg = i2c_smbus_read_byte_data(client, MAX6650_REG_ALARM_EN);
|
||||
if (reg < 0) {
|
||||
dev_err(dev, "Failed to read alarm configuration, aborting.\n");
|
||||
return reg;
|
||||
}
|
||||
data->alarm_en = reg;
|
||||
|
||||
if (!of_property_read_u32(client->dev.of_node, "maxim,fan-target-rpm",
|
||||
&target_rpm)) {
|
||||
@ -684,8 +467,6 @@ static int max6650_init_client(struct max6650_data *data,
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if IS_ENABLED(CONFIG_THERMAL)
|
||||
|
||||
static int max6650_get_max_state(struct thermal_cooling_device *cdev,
|
||||
unsigned long *state)
|
||||
{
|
||||
@ -715,23 +496,18 @@ static int max6650_set_cur_state(struct thermal_cooling_device *cdev,
|
||||
|
||||
mutex_lock(&data->update_lock);
|
||||
|
||||
if (data->config & MAX6650_CFG_V12)
|
||||
data->dac = 180 - (180 * state)/255;
|
||||
else
|
||||
data->dac = 76 - (76 * state)/255;
|
||||
|
||||
data->dac = pwm_to_dac(state, data->config & MAX6650_CFG_V12);
|
||||
err = i2c_smbus_write_byte_data(client, MAX6650_REG_DAC, data->dac);
|
||||
|
||||
if (!err) {
|
||||
max6650_set_operating_mode(data, state ?
|
||||
MAX6650_CFG_MODE_OPEN_LOOP :
|
||||
MAX6650_CFG_MODE_OFF);
|
||||
MAX6650_CFG_MODE_OPEN_LOOP :
|
||||
MAX6650_CFG_MODE_OFF);
|
||||
data->cooling_dev_state = state;
|
||||
}
|
||||
|
||||
mutex_unlock(&data->update_lock);
|
||||
|
||||
return err < 0 ? err : 0;
|
||||
return err;
|
||||
}
|
||||
|
||||
static const struct thermal_cooling_device_ops max6650_cooling_ops = {
|
||||
@ -739,11 +515,252 @@ static const struct thermal_cooling_device_ops max6650_cooling_ops = {
|
||||
.get_cur_state = max6650_get_cur_state,
|
||||
.set_cur_state = max6650_set_cur_state,
|
||||
};
|
||||
#endif
|
||||
|
||||
static int max6650_read(struct device *dev, enum hwmon_sensor_types type,
|
||||
u32 attr, int channel, long *val)
|
||||
{
|
||||
struct max6650_data *data = max6650_update_device(dev);
|
||||
int mode;
|
||||
|
||||
if (IS_ERR(data))
|
||||
return PTR_ERR(data);
|
||||
|
||||
switch (type) {
|
||||
case hwmon_pwm:
|
||||
switch (attr) {
|
||||
case hwmon_pwm_input:
|
||||
*val = dac_to_pwm(data->dac,
|
||||
data->config & MAX6650_CFG_V12);
|
||||
break;
|
||||
case hwmon_pwm_enable:
|
||||
/*
|
||||
* Possible values:
|
||||
* 0 = Fan always on
|
||||
* 1 = Open loop, Voltage is set according to speed,
|
||||
* not regulated.
|
||||
* 2 = Closed loop, RPM for all fans regulated by fan1
|
||||
* tachometer
|
||||
* 3 = Fan off
|
||||
*/
|
||||
mode = (data->config & MAX6650_CFG_MODE_MASK) >> 4;
|
||||
*val = (4 - mode) & 3; /* {0 1 2 3} -> {0 3 2 1} */
|
||||
break;
|
||||
default:
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
break;
|
||||
case hwmon_fan:
|
||||
switch (attr) {
|
||||
case hwmon_fan_input:
|
||||
/*
|
||||
* Calculation details:
|
||||
*
|
||||
* Each tachometer counts over an interval given by the
|
||||
* "count" register (0.25, 0.5, 1 or 2 seconds).
|
||||
* The driver assumes that the fans produce two pulses
|
||||
* per revolution (this seems to be the most common).
|
||||
*/
|
||||
*val = DIV_ROUND_CLOSEST(data->tach[channel] * 120,
|
||||
DIV_FROM_REG(data->count));
|
||||
break;
|
||||
case hwmon_fan_div:
|
||||
*val = DIV_FROM_REG(data->count);
|
||||
break;
|
||||
case hwmon_fan_target:
|
||||
/*
|
||||
* Use the datasheet equation:
|
||||
* FanSpeed = KSCALE x fCLK / [256 x (KTACH + 1)]
|
||||
* then multiply by 60 to give rpm.
|
||||
*/
|
||||
*val = 60 * DIV_FROM_REG(data->config) * clock /
|
||||
(256 * (data->speed + 1));
|
||||
break;
|
||||
case hwmon_fan_min_alarm:
|
||||
*val = !!(data->alarm & MAX6650_ALRM_MIN);
|
||||
data->alarm &= ~MAX6650_ALRM_MIN;
|
||||
data->valid = false;
|
||||
break;
|
||||
case hwmon_fan_max_alarm:
|
||||
*val = !!(data->alarm & MAX6650_ALRM_MAX);
|
||||
data->alarm &= ~MAX6650_ALRM_MAX;
|
||||
data->valid = false;
|
||||
break;
|
||||
case hwmon_fan_fault:
|
||||
*val = !!(data->alarm & MAX6650_ALRM_TACH);
|
||||
data->alarm &= ~MAX6650_ALRM_TACH;
|
||||
data->valid = false;
|
||||
break;
|
||||
default:
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const u8 max6650_pwm_modes[] = {
|
||||
MAX6650_CFG_MODE_ON,
|
||||
MAX6650_CFG_MODE_OPEN_LOOP,
|
||||
MAX6650_CFG_MODE_CLOSED_LOOP,
|
||||
MAX6650_CFG_MODE_OFF,
|
||||
};
|
||||
|
||||
static int max6650_write(struct device *dev, enum hwmon_sensor_types type,
|
||||
u32 attr, int channel, long val)
|
||||
{
|
||||
struct max6650_data *data = dev_get_drvdata(dev);
|
||||
int ret = 0;
|
||||
u8 reg;
|
||||
|
||||
mutex_lock(&data->update_lock);
|
||||
|
||||
switch (type) {
|
||||
case hwmon_pwm:
|
||||
switch (attr) {
|
||||
case hwmon_pwm_input:
|
||||
reg = pwm_to_dac(clamp_val(val, 0, 255),
|
||||
data->config & MAX6650_CFG_V12);
|
||||
ret = i2c_smbus_write_byte_data(data->client,
|
||||
MAX6650_REG_DAC, reg);
|
||||
if (ret)
|
||||
break;
|
||||
data->dac = reg;
|
||||
break;
|
||||
case hwmon_pwm_enable:
|
||||
if (val < 0 || val >= ARRAY_SIZE(max6650_pwm_modes)) {
|
||||
ret = -EINVAL;
|
||||
break;
|
||||
}
|
||||
ret = max6650_set_operating_mode(data,
|
||||
max6650_pwm_modes[val]);
|
||||
break;
|
||||
default:
|
||||
ret = -EOPNOTSUPP;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case hwmon_fan:
|
||||
switch (attr) {
|
||||
case hwmon_fan_div:
|
||||
switch (val) {
|
||||
case 1:
|
||||
reg = 0;
|
||||
break;
|
||||
case 2:
|
||||
reg = 1;
|
||||
break;
|
||||
case 4:
|
||||
reg = 2;
|
||||
break;
|
||||
case 8:
|
||||
reg = 3;
|
||||
break;
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
goto error;
|
||||
}
|
||||
ret = i2c_smbus_write_byte_data(data->client,
|
||||
MAX6650_REG_COUNT, reg);
|
||||
if (ret)
|
||||
break;
|
||||
data->count = reg;
|
||||
break;
|
||||
case hwmon_fan_target:
|
||||
if (val < 0) {
|
||||
ret = -EINVAL;
|
||||
break;
|
||||
}
|
||||
ret = max6650_set_target(data, val);
|
||||
break;
|
||||
default:
|
||||
ret = -EOPNOTSUPP;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
ret = -EOPNOTSUPP;
|
||||
break;
|
||||
}
|
||||
|
||||
error:
|
||||
mutex_unlock(&data->update_lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static umode_t max6650_is_visible(const void *_data,
|
||||
enum hwmon_sensor_types type, u32 attr,
|
||||
int channel)
|
||||
{
|
||||
const struct max6650_data *data = _data;
|
||||
|
||||
if (channel && (channel >= data->nr_fans || type != hwmon_fan))
|
||||
return 0;
|
||||
|
||||
switch (type) {
|
||||
case hwmon_fan:
|
||||
switch (attr) {
|
||||
case hwmon_fan_input:
|
||||
return 0444;
|
||||
case hwmon_fan_target:
|
||||
case hwmon_fan_div:
|
||||
return 0644;
|
||||
case hwmon_fan_min_alarm:
|
||||
if (data->alarm_en & MAX6650_ALRM_MIN)
|
||||
return 0444;
|
||||
break;
|
||||
case hwmon_fan_max_alarm:
|
||||
if (data->alarm_en & MAX6650_ALRM_MAX)
|
||||
return 0444;
|
||||
break;
|
||||
case hwmon_fan_fault:
|
||||
if (data->alarm_en & MAX6650_ALRM_TACH)
|
||||
return 0444;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case hwmon_pwm:
|
||||
switch (attr) {
|
||||
case hwmon_pwm_input:
|
||||
case hwmon_pwm_enable:
|
||||
return 0644;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct hwmon_channel_info *max6650_info[] = {
|
||||
HWMON_CHANNEL_INFO(fan, HWMON_F_INPUT | HWMON_F_TARGET | HWMON_F_DIV |
|
||||
HWMON_F_MIN_ALARM | HWMON_F_MAX_ALARM |
|
||||
HWMON_F_FAULT,
|
||||
HWMON_F_INPUT, HWMON_F_INPUT, HWMON_F_INPUT),
|
||||
HWMON_CHANNEL_INFO(pwm, HWMON_PWM_INPUT | HWMON_PWM_ENABLE),
|
||||
NULL
|
||||
};
|
||||
|
||||
static const struct hwmon_ops max6650_hwmon_ops = {
|
||||
.read = max6650_read,
|
||||
.write = max6650_write,
|
||||
.is_visible = max6650_is_visible,
|
||||
};
|
||||
|
||||
static const struct hwmon_chip_info max6650_chip_info = {
|
||||
.ops = &max6650_hwmon_ops,
|
||||
.info = max6650_info,
|
||||
};
|
||||
|
||||
static int max6650_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
struct thermal_cooling_device *cooling_dev;
|
||||
struct device *dev = &client->dev;
|
||||
const struct of_device_id *of_id =
|
||||
of_match_device(of_match_ptr(max6650_dt_match), dev);
|
||||
@ -767,37 +784,23 @@ static int max6650_probe(struct i2c_client *client,
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
data->groups[0] = &max6650_group;
|
||||
/* 3 additional fan inputs for the MAX6651 */
|
||||
if (data->nr_fans == 4)
|
||||
data->groups[1] = &max6651_group;
|
||||
|
||||
hwmon_dev = devm_hwmon_device_register_with_groups(dev,
|
||||
client->name, data,
|
||||
data->groups);
|
||||
hwmon_dev = devm_hwmon_device_register_with_info(dev,
|
||||
client->name, data,
|
||||
&max6650_chip_info,
|
||||
max6650_groups);
|
||||
err = PTR_ERR_OR_ZERO(hwmon_dev);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
#if IS_ENABLED(CONFIG_THERMAL)
|
||||
data->cooling_dev =
|
||||
thermal_of_cooling_device_register(client->dev.of_node,
|
||||
client->name, data,
|
||||
&max6650_cooling_ops);
|
||||
if (IS_ERR(data->cooling_dev))
|
||||
dev_warn(&client->dev,
|
||||
"thermal cooling device register failed: %ld\n",
|
||||
PTR_ERR(data->cooling_dev));
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int max6650_remove(struct i2c_client *client)
|
||||
{
|
||||
struct max6650_data *data = i2c_get_clientdata(client);
|
||||
|
||||
if (!IS_ERR(data->cooling_dev))
|
||||
thermal_cooling_device_unregister(data->cooling_dev);
|
||||
if (IS_ENABLED(CONFIG_THERMAL)) {
|
||||
cooling_dev = devm_thermal_of_cooling_device_register(dev,
|
||||
dev->of_node, client->name,
|
||||
data, &max6650_cooling_ops);
|
||||
if (IS_ERR(cooling_dev)) {
|
||||
dev_warn(dev, "thermal cooling device register failed: %ld\n",
|
||||
PTR_ERR(cooling_dev));
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -815,7 +818,6 @@ static struct i2c_driver max6650_driver = {
|
||||
.of_match_table = of_match_ptr(max6650_dt_match),
|
||||
},
|
||||
.probe = max6650_probe,
|
||||
.remove = max6650_remove,
|
||||
.id_table = max6650_id,
|
||||
};
|
||||
|
||||
|
@ -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;
|
||||
data->tcpu_mask = ((ret >> 8) & 0xf) | ((ret & 0xf) << 4);
|
||||
|
||||
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;
|
||||
|
||||
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;
|
||||
|
||||
|
67
drivers/hwmon/pmbus/irps5401.c
Normal file
67
drivers/hwmon/pmbus/irps5401.c
Normal file
@ -0,0 +1,67 @@
|
||||
// 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");
|
139
drivers/hwmon/pmbus/pxe1610.c
Normal file
139
drivers/hwmon/pmbus/pxe1610.c
Normal file
@ -0,0 +1,139 @@
|
||||
// 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;
|
||||
|
@ -1,17 +1,9 @@
|
||||
// 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 */
|
||||
|
Loading…
Reference in New Issue
Block a user