hwmon fixes for v7.0-rc6
- PMBus driver fixes
* Add mutex protection for regulator operations
* Fix reading from "write-only" attributes
* Mark lowest/average/highest/rated attributes as read-only
* isl68137: Add mutex protection for AVS enable sysfs attributes
* ina233: Fix error handling and sign extension when reading shunt voltage
- adm1177: Fix sysfs ABI violation and current unit conversion
- peci: Fix off-by-one in cputemp_is_visible(), and crit_hyst returning
delta instead of absolute temperature
-----BEGIN PGP SIGNATURE-----
iQIzBAABCAAdFiEEiHPvMQj9QTOCiqgVyx8mb86fmYEFAmnHFFEACgkQyx8mb86f
mYEUJQ//anhuaDrLyAQUU5mtgoQu53gazUEW8mg2jIYKrQJx6nXpWuKWcgoASDRX
YJbvIydm9VFOrXLhfefncSI3xTgyB/PQV0gnNMZBwSxXX4Fw+zAXiH7aS6haUp82
BuM09yreaNTqa1v5b+5Av13BPnL+ZbiEfyoIhCRKtaI0UlBgoDD+DRT3EkXwubnE
CVFKptgD5BvSTWgO/wmOdOiBCJNfxeH/xwIY4INsInDwFwQP8F1tF+m9EB1mkbII
LG/kjwiNv1Xqf/CCsJnlMN7rYLlb3KvhdMcMtB380ImX3tJ+SP35c1+ZJ9DP+es9
2dScRBThI33NaqAz2l0wpqhDjuY/KjIAjpxIcRJpR4hlWr/vVQwoGyRBofNkasIO
+aV3mHP6qbVhwFtcGZ5MqU7AOnBOJTim9KqaBoZAR8p6VnViTUXN2wgwZyU3NnXO
I9f0zIpagw7I0gP3WWbMv2++PVnEP/+Huc0aZtIeFVqcR17TFp9Zj+nILa0guuvq
ZxgdR/2BXfEgMNNIKyleIXuXLvuPFQ6MH+cf/roVeC7XKtdYeVh3LUJdtlVRT8xO
2xrCbKScuAnzrmKPpqkSESU7VUJ2F6hFFl8DVUY8UZgv/DQWFvEQjf9r0mzHs0eb
lpmh1BPqp4V8KhChef80tGBqMuNezXklY4IQcUvKyLuuRY0WOpM=
=dKNH
-----END PGP SIGNATURE-----
Merge tag 'hwmon-for-v7.0-rc6' of git://git.kernel.org/pub/scm/linux/kernel/git/groeck/linux-staging
Pull hwmon fixes from Guenter Roeck:
- PMBus driver fixes:
- Add mutex protection for regulator operations
- Fix reading from "write-only" attributes
- Mark lowest/average/highest/rated attributes as read-only
- isl68137: Add mutex protection for AVS enable sysfs attributes
- ina233: Fix error handling and sign extension when reading shunt voltage
- adm1177: Fix sysfs ABI violation and current unit conversion
- peci: Fix off-by-one in cputemp_is_visible(), and crit_hyst returning
delta instead of absolute temperature
* tag 'hwmon-for-v7.0-rc6' of git://git.kernel.org/pub/scm/linux/kernel/git/groeck/linux-staging:
hwmon: (pmbus/core) Protect regulator operations with mutex
hwmon: (pmbus) Introduce the concept of "write-only" attributes
hwmon: (pmbus) Mark lowest/average/highest/rated attributes as read-only
hwmon: (adm1177) fix sysfs ABI violation and current unit conversion
hwmon: (peci/cputemp) Fix off-by-one in cputemp_is_visible()
hwmon: (peci/cputemp) Fix crit_hyst returning delta instead of absolute temperature
hwmon: (pmbus/isl68137) Add mutex protection for AVS enable sysfs attributes
hwmon: (pmbus/ina233) Fix error handling and sign extension in shunt voltage read
master
commit
be762d8b6d
|
|
@ -27,10 +27,10 @@ for details.
|
|||
Sysfs entries
|
||||
-------------
|
||||
|
||||
The following attributes are supported. Current maxim attribute
|
||||
The following attributes are supported. Current maximum attribute
|
||||
is read-write, all other attributes are read-only.
|
||||
|
||||
in0_input Measured voltage in microvolts.
|
||||
in0_input Measured voltage in millivolts.
|
||||
|
||||
curr1_input Measured current in microamperes.
|
||||
curr1_max_alarm Overcurrent alarm in microamperes.
|
||||
curr1_input Measured current in milliamperes.
|
||||
curr1_max Overcurrent shutdown threshold in milliamperes.
|
||||
|
|
|
|||
|
|
@ -51,8 +51,9 @@ temp1_max Provides thermal control temperature of the CPU package
|
|||
temp1_crit Provides shutdown temperature of the CPU package which
|
||||
is also known as the maximum processor junction
|
||||
temperature, Tjmax or Tprochot.
|
||||
temp1_crit_hyst Provides the hysteresis value from Tcontrol to Tjmax of
|
||||
the CPU package.
|
||||
temp1_crit_hyst Provides the hysteresis temperature of the CPU
|
||||
package. Returns Tcontrol, the temperature at which
|
||||
the critical condition clears.
|
||||
|
||||
temp2_label "DTS"
|
||||
temp2_input Provides current temperature of the CPU package scaled
|
||||
|
|
@ -62,8 +63,9 @@ temp2_max Provides thermal control temperature of the CPU package
|
|||
temp2_crit Provides shutdown temperature of the CPU package which
|
||||
is also known as the maximum processor junction
|
||||
temperature, Tjmax or Tprochot.
|
||||
temp2_crit_hyst Provides the hysteresis value from Tcontrol to Tjmax of
|
||||
the CPU package.
|
||||
temp2_crit_hyst Provides the hysteresis temperature of the CPU
|
||||
package. Returns Tcontrol, the temperature at which
|
||||
the critical condition clears.
|
||||
|
||||
temp3_label "Tcontrol"
|
||||
temp3_input Provides current Tcontrol temperature of the CPU
|
||||
|
|
|
|||
|
|
@ -10,6 +10,8 @@
|
|||
#include <linux/hwmon.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/math64.h>
|
||||
#include <linux/minmax.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
|
||||
|
|
@ -33,7 +35,7 @@
|
|||
struct adm1177_state {
|
||||
struct i2c_client *client;
|
||||
u32 r_sense_uohm;
|
||||
u32 alert_threshold_ua;
|
||||
u64 alert_threshold_ua;
|
||||
bool vrange_high;
|
||||
};
|
||||
|
||||
|
|
@ -48,7 +50,7 @@ static int adm1177_write_cmd(struct adm1177_state *st, u8 cmd)
|
|||
}
|
||||
|
||||
static int adm1177_write_alert_thr(struct adm1177_state *st,
|
||||
u32 alert_threshold_ua)
|
||||
u64 alert_threshold_ua)
|
||||
{
|
||||
u64 val;
|
||||
int ret;
|
||||
|
|
@ -91,8 +93,8 @@ static int adm1177_read(struct device *dev, enum hwmon_sensor_types type,
|
|||
*val = div_u64((105840000ull * dummy),
|
||||
4096 * st->r_sense_uohm);
|
||||
return 0;
|
||||
case hwmon_curr_max_alarm:
|
||||
*val = st->alert_threshold_ua;
|
||||
case hwmon_curr_max:
|
||||
*val = div_u64(st->alert_threshold_ua, 1000);
|
||||
return 0;
|
||||
default:
|
||||
return -EOPNOTSUPP;
|
||||
|
|
@ -126,9 +128,10 @@ static int adm1177_write(struct device *dev, enum hwmon_sensor_types type,
|
|||
switch (type) {
|
||||
case hwmon_curr:
|
||||
switch (attr) {
|
||||
case hwmon_curr_max_alarm:
|
||||
adm1177_write_alert_thr(st, val);
|
||||
return 0;
|
||||
case hwmon_curr_max:
|
||||
val = clamp_val(val, 0,
|
||||
div_u64(105840000ULL, st->r_sense_uohm));
|
||||
return adm1177_write_alert_thr(st, (u64)val * 1000);
|
||||
default:
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
|
@ -156,7 +159,7 @@ static umode_t adm1177_is_visible(const void *data,
|
|||
if (st->r_sense_uohm)
|
||||
return 0444;
|
||||
return 0;
|
||||
case hwmon_curr_max_alarm:
|
||||
case hwmon_curr_max:
|
||||
if (st->r_sense_uohm)
|
||||
return 0644;
|
||||
return 0;
|
||||
|
|
@ -170,7 +173,7 @@ static umode_t adm1177_is_visible(const void *data,
|
|||
|
||||
static const struct hwmon_channel_info * const adm1177_info[] = {
|
||||
HWMON_CHANNEL_INFO(curr,
|
||||
HWMON_C_INPUT | HWMON_C_MAX_ALARM),
|
||||
HWMON_C_INPUT | HWMON_C_MAX),
|
||||
HWMON_CHANNEL_INFO(in,
|
||||
HWMON_I_INPUT),
|
||||
NULL
|
||||
|
|
@ -192,7 +195,8 @@ static int adm1177_probe(struct i2c_client *client)
|
|||
struct device *dev = &client->dev;
|
||||
struct device *hwmon_dev;
|
||||
struct adm1177_state *st;
|
||||
u32 alert_threshold_ua;
|
||||
u64 alert_threshold_ua;
|
||||
u32 prop;
|
||||
int ret;
|
||||
|
||||
st = devm_kzalloc(dev, sizeof(*st), GFP_KERNEL);
|
||||
|
|
@ -208,22 +212,26 @@ static int adm1177_probe(struct i2c_client *client)
|
|||
if (device_property_read_u32(dev, "shunt-resistor-micro-ohms",
|
||||
&st->r_sense_uohm))
|
||||
st->r_sense_uohm = 0;
|
||||
if (device_property_read_u32(dev, "adi,shutdown-threshold-microamp",
|
||||
&alert_threshold_ua)) {
|
||||
if (st->r_sense_uohm)
|
||||
/*
|
||||
* set maximum default value from datasheet based on
|
||||
* shunt-resistor
|
||||
*/
|
||||
alert_threshold_ua = div_u64(105840000000,
|
||||
st->r_sense_uohm);
|
||||
else
|
||||
alert_threshold_ua = 0;
|
||||
if (!device_property_read_u32(dev, "adi,shutdown-threshold-microamp",
|
||||
&prop)) {
|
||||
alert_threshold_ua = prop;
|
||||
} else if (st->r_sense_uohm) {
|
||||
/*
|
||||
* set maximum default value from datasheet based on
|
||||
* shunt-resistor
|
||||
*/
|
||||
alert_threshold_ua = div_u64(105840000000ULL,
|
||||
st->r_sense_uohm);
|
||||
} else {
|
||||
alert_threshold_ua = 0;
|
||||
}
|
||||
st->vrange_high = device_property_read_bool(dev,
|
||||
"adi,vrange-high-enable");
|
||||
if (alert_threshold_ua && st->r_sense_uohm)
|
||||
adm1177_write_alert_thr(st, alert_threshold_ua);
|
||||
if (alert_threshold_ua && st->r_sense_uohm) {
|
||||
ret = adm1177_write_alert_thr(st, alert_threshold_ua);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = adm1177_write_cmd(st, ADM1177_CMD_V_CONT |
|
||||
ADM1177_CMD_I_CONT |
|
||||
|
|
|
|||
|
|
@ -131,7 +131,7 @@ static int get_temp_target(struct peci_cputemp *priv, enum peci_temp_target_type
|
|||
*val = priv->temp.target.tjmax;
|
||||
break;
|
||||
case crit_hyst_type:
|
||||
*val = priv->temp.target.tjmax - priv->temp.target.tcontrol;
|
||||
*val = priv->temp.target.tcontrol;
|
||||
break;
|
||||
default:
|
||||
ret = -EOPNOTSUPP;
|
||||
|
|
@ -319,7 +319,7 @@ static umode_t cputemp_is_visible(const void *data, enum hwmon_sensor_types type
|
|||
{
|
||||
const struct peci_cputemp *priv = data;
|
||||
|
||||
if (channel > CPUTEMP_CHANNEL_NUMS)
|
||||
if (channel >= CPUTEMP_CHANNEL_NUMS)
|
||||
return 0;
|
||||
|
||||
if (channel < channel_core)
|
||||
|
|
|
|||
|
|
@ -72,7 +72,8 @@ static int ina233_read_word_data(struct i2c_client *client, int page,
|
|||
|
||||
/* Adjust returned value to match VIN coefficients */
|
||||
/* VIN: 1.25 mV VSHUNT: 2.5 uV LSB */
|
||||
ret = DIV_ROUND_CLOSEST(ret * 25, 12500);
|
||||
ret = clamp_val(DIV_ROUND_CLOSEST((s16)ret * 25, 12500),
|
||||
S16_MIN, S16_MAX) & 0xffff;
|
||||
break;
|
||||
default:
|
||||
ret = -ENODATA;
|
||||
|
|
|
|||
|
|
@ -96,7 +96,15 @@ static ssize_t isl68137_avs_enable_show_page(struct i2c_client *client,
|
|||
int page,
|
||||
char *buf)
|
||||
{
|
||||
int val = pmbus_read_byte_data(client, page, PMBUS_OPERATION);
|
||||
int val;
|
||||
|
||||
val = pmbus_lock_interruptible(client);
|
||||
if (val)
|
||||
return val;
|
||||
|
||||
val = pmbus_read_byte_data(client, page, PMBUS_OPERATION);
|
||||
|
||||
pmbus_unlock(client);
|
||||
|
||||
if (val < 0)
|
||||
return val;
|
||||
|
|
@ -118,6 +126,10 @@ static ssize_t isl68137_avs_enable_store_page(struct i2c_client *client,
|
|||
|
||||
op_val = result ? ISL68137_VOUT_AVS : 0;
|
||||
|
||||
rc = pmbus_lock_interruptible(client);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
/*
|
||||
* Writes to VOUT setpoint over AVSBus will persist after the VRM is
|
||||
* switched to PMBus control. Switching back to AVSBus control
|
||||
|
|
@ -129,17 +141,20 @@ static ssize_t isl68137_avs_enable_store_page(struct i2c_client *client,
|
|||
rc = pmbus_read_word_data(client, page, 0xff,
|
||||
PMBUS_VOUT_COMMAND);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
goto unlock;
|
||||
|
||||
rc = pmbus_write_word_data(client, page, PMBUS_VOUT_COMMAND,
|
||||
rc);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
rc = pmbus_update_byte_data(client, page, PMBUS_OPERATION,
|
||||
ISL68137_VOUT_AVS, op_val);
|
||||
|
||||
unlock:
|
||||
pmbus_unlock(client);
|
||||
|
||||
return (rc < 0) ? rc : count;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@
|
|||
* Copyright (c) 2012 Guenter Roeck
|
||||
*/
|
||||
|
||||
#include <linux/atomic.h>
|
||||
#include <linux/debugfs.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/dcache.h>
|
||||
|
|
@ -21,8 +22,8 @@
|
|||
#include <linux/pmbus.h>
|
||||
#include <linux/regulator/driver.h>
|
||||
#include <linux/regulator/machine.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/thermal.h>
|
||||
#include <linux/workqueue.h>
|
||||
#include "pmbus.h"
|
||||
|
||||
/*
|
||||
|
|
@ -112,6 +113,11 @@ struct pmbus_data {
|
|||
|
||||
struct mutex update_lock;
|
||||
|
||||
#if IS_ENABLED(CONFIG_REGULATOR)
|
||||
atomic_t regulator_events[PMBUS_PAGES];
|
||||
struct work_struct regulator_notify_work;
|
||||
#endif
|
||||
|
||||
bool has_status_word; /* device uses STATUS_WORD register */
|
||||
int (*read_status)(struct i2c_client *client, int page);
|
||||
|
||||
|
|
@ -1209,6 +1215,12 @@ static ssize_t pmbus_show_boolean(struct device *dev,
|
|||
return sysfs_emit(buf, "%d\n", val);
|
||||
}
|
||||
|
||||
static ssize_t pmbus_show_zero(struct device *dev,
|
||||
struct device_attribute *devattr, char *buf)
|
||||
{
|
||||
return sysfs_emit(buf, "0\n");
|
||||
}
|
||||
|
||||
static ssize_t pmbus_show_sensor(struct device *dev,
|
||||
struct device_attribute *devattr, char *buf)
|
||||
{
|
||||
|
|
@ -1407,7 +1419,7 @@ static struct pmbus_sensor *pmbus_add_sensor(struct pmbus_data *data,
|
|||
int reg,
|
||||
enum pmbus_sensor_classes class,
|
||||
bool update, bool readonly,
|
||||
bool convert)
|
||||
bool writeonly, bool convert)
|
||||
{
|
||||
struct pmbus_sensor *sensor;
|
||||
struct device_attribute *a;
|
||||
|
|
@ -1436,7 +1448,8 @@ static struct pmbus_sensor *pmbus_add_sensor(struct pmbus_data *data,
|
|||
sensor->data = -ENODATA;
|
||||
pmbus_dev_attr_init(a, sensor->name,
|
||||
readonly ? 0444 : 0644,
|
||||
pmbus_show_sensor, pmbus_set_sensor);
|
||||
writeonly ? pmbus_show_zero : pmbus_show_sensor,
|
||||
pmbus_set_sensor);
|
||||
|
||||
if (pmbus_add_attribute(data, &a->attr))
|
||||
return NULL;
|
||||
|
|
@ -1495,8 +1508,10 @@ static int pmbus_add_label(struct pmbus_data *data,
|
|||
struct pmbus_limit_attr {
|
||||
u16 reg; /* Limit register */
|
||||
u16 sbit; /* Alarm attribute status bit */
|
||||
bool update; /* True if register needs updates */
|
||||
bool low; /* True if low limit; for limits with compare functions only */
|
||||
bool readonly:1; /* True if the attribute is read-only */
|
||||
bool writeonly:1; /* True if the attribute is write-only */
|
||||
bool update:1; /* True if register needs updates */
|
||||
bool low:1; /* True if low limit; for limits with compare functions only */
|
||||
const char *attr; /* Attribute name */
|
||||
const char *alarm; /* Alarm attribute name */
|
||||
};
|
||||
|
|
@ -1511,9 +1526,9 @@ struct pmbus_sensor_attr {
|
|||
u8 nlimit; /* # of limit registers */
|
||||
enum pmbus_sensor_classes class;/* sensor class */
|
||||
const char *label; /* sensor label */
|
||||
bool paged; /* true if paged sensor */
|
||||
bool update; /* true if update needed */
|
||||
bool compare; /* true if compare function needed */
|
||||
bool paged:1; /* true if paged sensor */
|
||||
bool update:1; /* true if update needed */
|
||||
bool compare:1; /* true if compare function needed */
|
||||
u32 func; /* sensor mask */
|
||||
u32 sfunc; /* sensor status mask */
|
||||
int sreg; /* status register */
|
||||
|
|
@ -1544,7 +1559,7 @@ static int pmbus_add_limit_attrs(struct i2c_client *client,
|
|||
curr = pmbus_add_sensor(data, name, l->attr, index,
|
||||
page, 0xff, l->reg, attr->class,
|
||||
attr->update || l->update,
|
||||
false, true);
|
||||
l->readonly, l->writeonly, true);
|
||||
if (!curr)
|
||||
return -ENOMEM;
|
||||
if (l->sbit && (info->func[page] & attr->sfunc)) {
|
||||
|
|
@ -1584,7 +1599,7 @@ static int pmbus_add_sensor_attrs_one(struct i2c_client *client,
|
|||
return ret;
|
||||
}
|
||||
base = pmbus_add_sensor(data, name, "input", index, page, phase,
|
||||
attr->reg, attr->class, true, true, true);
|
||||
attr->reg, attr->class, true, true, false, true);
|
||||
if (!base)
|
||||
return -ENOMEM;
|
||||
/* No limit and alarm attributes for phase specific sensors */
|
||||
|
|
@ -1707,23 +1722,29 @@ static const struct pmbus_limit_attr vin_limit_attrs[] = {
|
|||
}, {
|
||||
.reg = PMBUS_VIRT_READ_VIN_AVG,
|
||||
.update = true,
|
||||
.readonly = true,
|
||||
.attr = "average",
|
||||
}, {
|
||||
.reg = PMBUS_VIRT_READ_VIN_MIN,
|
||||
.update = true,
|
||||
.readonly = true,
|
||||
.attr = "lowest",
|
||||
}, {
|
||||
.reg = PMBUS_VIRT_READ_VIN_MAX,
|
||||
.update = true,
|
||||
.readonly = true,
|
||||
.attr = "highest",
|
||||
}, {
|
||||
.reg = PMBUS_VIRT_RESET_VIN_HISTORY,
|
||||
.writeonly = true,
|
||||
.attr = "reset_history",
|
||||
}, {
|
||||
.reg = PMBUS_MFR_VIN_MIN,
|
||||
.readonly = true,
|
||||
.attr = "rated_min",
|
||||
}, {
|
||||
.reg = PMBUS_MFR_VIN_MAX,
|
||||
.readonly = true,
|
||||
.attr = "rated_max",
|
||||
},
|
||||
};
|
||||
|
|
@ -1776,23 +1797,29 @@ static const struct pmbus_limit_attr vout_limit_attrs[] = {
|
|||
}, {
|
||||
.reg = PMBUS_VIRT_READ_VOUT_AVG,
|
||||
.update = true,
|
||||
.readonly = true,
|
||||
.attr = "average",
|
||||
}, {
|
||||
.reg = PMBUS_VIRT_READ_VOUT_MIN,
|
||||
.update = true,
|
||||
.readonly = true,
|
||||
.attr = "lowest",
|
||||
}, {
|
||||
.reg = PMBUS_VIRT_READ_VOUT_MAX,
|
||||
.update = true,
|
||||
.readonly = true,
|
||||
.attr = "highest",
|
||||
}, {
|
||||
.reg = PMBUS_VIRT_RESET_VOUT_HISTORY,
|
||||
.writeonly = true,
|
||||
.attr = "reset_history",
|
||||
}, {
|
||||
.reg = PMBUS_MFR_VOUT_MIN,
|
||||
.readonly = true,
|
||||
.attr = "rated_min",
|
||||
}, {
|
||||
.reg = PMBUS_MFR_VOUT_MAX,
|
||||
.readonly = true,
|
||||
.attr = "rated_max",
|
||||
},
|
||||
};
|
||||
|
|
@ -1852,20 +1879,25 @@ static const struct pmbus_limit_attr iin_limit_attrs[] = {
|
|||
}, {
|
||||
.reg = PMBUS_VIRT_READ_IIN_AVG,
|
||||
.update = true,
|
||||
.readonly = true,
|
||||
.attr = "average",
|
||||
}, {
|
||||
.reg = PMBUS_VIRT_READ_IIN_MIN,
|
||||
.update = true,
|
||||
.readonly = true,
|
||||
.attr = "lowest",
|
||||
}, {
|
||||
.reg = PMBUS_VIRT_READ_IIN_MAX,
|
||||
.update = true,
|
||||
.readonly = true,
|
||||
.attr = "highest",
|
||||
}, {
|
||||
.reg = PMBUS_VIRT_RESET_IIN_HISTORY,
|
||||
.writeonly = true,
|
||||
.attr = "reset_history",
|
||||
}, {
|
||||
.reg = PMBUS_MFR_IIN_MAX,
|
||||
.readonly = true,
|
||||
.attr = "rated_max",
|
||||
},
|
||||
};
|
||||
|
|
@ -1889,20 +1921,25 @@ static const struct pmbus_limit_attr iout_limit_attrs[] = {
|
|||
}, {
|
||||
.reg = PMBUS_VIRT_READ_IOUT_AVG,
|
||||
.update = true,
|
||||
.readonly = true,
|
||||
.attr = "average",
|
||||
}, {
|
||||
.reg = PMBUS_VIRT_READ_IOUT_MIN,
|
||||
.update = true,
|
||||
.readonly = true,
|
||||
.attr = "lowest",
|
||||
}, {
|
||||
.reg = PMBUS_VIRT_READ_IOUT_MAX,
|
||||
.update = true,
|
||||
.readonly = true,
|
||||
.attr = "highest",
|
||||
}, {
|
||||
.reg = PMBUS_VIRT_RESET_IOUT_HISTORY,
|
||||
.writeonly = true,
|
||||
.attr = "reset_history",
|
||||
}, {
|
||||
.reg = PMBUS_MFR_IOUT_MAX,
|
||||
.readonly = true,
|
||||
.attr = "rated_max",
|
||||
},
|
||||
};
|
||||
|
|
@ -1943,20 +1980,25 @@ static const struct pmbus_limit_attr pin_limit_attrs[] = {
|
|||
}, {
|
||||
.reg = PMBUS_VIRT_READ_PIN_AVG,
|
||||
.update = true,
|
||||
.readonly = true,
|
||||
.attr = "average",
|
||||
}, {
|
||||
.reg = PMBUS_VIRT_READ_PIN_MIN,
|
||||
.update = true,
|
||||
.readonly = true,
|
||||
.attr = "input_lowest",
|
||||
}, {
|
||||
.reg = PMBUS_VIRT_READ_PIN_MAX,
|
||||
.update = true,
|
||||
.readonly = true,
|
||||
.attr = "input_highest",
|
||||
}, {
|
||||
.reg = PMBUS_VIRT_RESET_PIN_HISTORY,
|
||||
.writeonly = true,
|
||||
.attr = "reset_history",
|
||||
}, {
|
||||
.reg = PMBUS_MFR_PIN_MAX,
|
||||
.readonly = true,
|
||||
.attr = "rated_max",
|
||||
},
|
||||
};
|
||||
|
|
@ -1980,20 +2022,25 @@ static const struct pmbus_limit_attr pout_limit_attrs[] = {
|
|||
}, {
|
||||
.reg = PMBUS_VIRT_READ_POUT_AVG,
|
||||
.update = true,
|
||||
.readonly = true,
|
||||
.attr = "average",
|
||||
}, {
|
||||
.reg = PMBUS_VIRT_READ_POUT_MIN,
|
||||
.update = true,
|
||||
.readonly = true,
|
||||
.attr = "input_lowest",
|
||||
}, {
|
||||
.reg = PMBUS_VIRT_READ_POUT_MAX,
|
||||
.update = true,
|
||||
.readonly = true,
|
||||
.attr = "input_highest",
|
||||
}, {
|
||||
.reg = PMBUS_VIRT_RESET_POUT_HISTORY,
|
||||
.writeonly = true,
|
||||
.attr = "reset_history",
|
||||
}, {
|
||||
.reg = PMBUS_MFR_POUT_MAX,
|
||||
.readonly = true,
|
||||
.attr = "rated_max",
|
||||
},
|
||||
};
|
||||
|
|
@ -2049,18 +2096,23 @@ static const struct pmbus_limit_attr temp_limit_attrs[] = {
|
|||
.sbit = PB_TEMP_OT_FAULT,
|
||||
}, {
|
||||
.reg = PMBUS_VIRT_READ_TEMP_MIN,
|
||||
.readonly = true,
|
||||
.attr = "lowest",
|
||||
}, {
|
||||
.reg = PMBUS_VIRT_READ_TEMP_AVG,
|
||||
.readonly = true,
|
||||
.attr = "average",
|
||||
}, {
|
||||
.reg = PMBUS_VIRT_READ_TEMP_MAX,
|
||||
.readonly = true,
|
||||
.attr = "highest",
|
||||
}, {
|
||||
.reg = PMBUS_VIRT_RESET_TEMP_HISTORY,
|
||||
.writeonly = true,
|
||||
.attr = "reset_history",
|
||||
}, {
|
||||
.reg = PMBUS_MFR_MAX_TEMP_1,
|
||||
.readonly = true,
|
||||
.attr = "rated_max",
|
||||
},
|
||||
};
|
||||
|
|
@ -2090,18 +2142,23 @@ static const struct pmbus_limit_attr temp_limit_attrs2[] = {
|
|||
.sbit = PB_TEMP_OT_FAULT,
|
||||
}, {
|
||||
.reg = PMBUS_VIRT_READ_TEMP2_MIN,
|
||||
.readonly = true,
|
||||
.attr = "lowest",
|
||||
}, {
|
||||
.reg = PMBUS_VIRT_READ_TEMP2_AVG,
|
||||
.readonly = true,
|
||||
.attr = "average",
|
||||
}, {
|
||||
.reg = PMBUS_VIRT_READ_TEMP2_MAX,
|
||||
.readonly = true,
|
||||
.attr = "highest",
|
||||
}, {
|
||||
.reg = PMBUS_VIRT_RESET_TEMP2_HISTORY,
|
||||
.writeonly = true,
|
||||
.attr = "reset_history",
|
||||
}, {
|
||||
.reg = PMBUS_MFR_MAX_TEMP_2,
|
||||
.readonly = true,
|
||||
.attr = "rated_max",
|
||||
},
|
||||
};
|
||||
|
|
@ -2131,6 +2188,7 @@ static const struct pmbus_limit_attr temp_limit_attrs3[] = {
|
|||
.sbit = PB_TEMP_OT_FAULT,
|
||||
}, {
|
||||
.reg = PMBUS_MFR_MAX_TEMP_3,
|
||||
.readonly = true,
|
||||
.attr = "rated_max",
|
||||
},
|
||||
};
|
||||
|
|
@ -2214,7 +2272,7 @@ static int pmbus_add_fan_ctrl(struct i2c_client *client,
|
|||
|
||||
sensor = pmbus_add_sensor(data, "fan", "target", index, page,
|
||||
0xff, PMBUS_VIRT_FAN_TARGET_1 + id, PSC_FAN,
|
||||
false, false, true);
|
||||
false, false, false, true);
|
||||
|
||||
if (!sensor)
|
||||
return -ENOMEM;
|
||||
|
|
@ -2225,14 +2283,14 @@ static int pmbus_add_fan_ctrl(struct i2c_client *client,
|
|||
|
||||
sensor = pmbus_add_sensor(data, "pwm", NULL, index, page,
|
||||
0xff, PMBUS_VIRT_PWM_1 + id, PSC_PWM,
|
||||
false, false, true);
|
||||
false, false, false, true);
|
||||
|
||||
if (!sensor)
|
||||
return -ENOMEM;
|
||||
|
||||
sensor = pmbus_add_sensor(data, "pwm", "enable", index, page,
|
||||
0xff, PMBUS_VIRT_PWM_ENABLE_1 + id, PSC_PWM,
|
||||
true, false, false);
|
||||
true, false, false, false);
|
||||
|
||||
if (!sensor)
|
||||
return -ENOMEM;
|
||||
|
|
@ -2274,7 +2332,7 @@ static int pmbus_add_fan_attributes(struct i2c_client *client,
|
|||
|
||||
if (pmbus_add_sensor(data, "fan", "input", index,
|
||||
page, 0xff, pmbus_fan_registers[f],
|
||||
PSC_FAN, true, true, true) == NULL)
|
||||
PSC_FAN, true, true, false, true) == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
/* Fan control */
|
||||
|
|
@ -3176,12 +3234,19 @@ static int pmbus_regulator_get_voltage(struct regulator_dev *rdev)
|
|||
.class = PSC_VOLTAGE_OUT,
|
||||
.convert = true,
|
||||
};
|
||||
int ret;
|
||||
|
||||
mutex_lock(&data->update_lock);
|
||||
s.data = _pmbus_read_word_data(client, s.page, 0xff, PMBUS_READ_VOUT);
|
||||
if (s.data < 0)
|
||||
return s.data;
|
||||
if (s.data < 0) {
|
||||
ret = s.data;
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
return (int)pmbus_reg2data(data, &s) * 1000; /* unit is uV */
|
||||
ret = (int)pmbus_reg2data(data, &s) * 1000; /* unit is uV */
|
||||
unlock:
|
||||
mutex_unlock(&data->update_lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int pmbus_regulator_set_voltage(struct regulator_dev *rdev, int min_uv,
|
||||
|
|
@ -3198,16 +3263,22 @@ static int pmbus_regulator_set_voltage(struct regulator_dev *rdev, int min_uv,
|
|||
};
|
||||
int val = DIV_ROUND_CLOSEST(min_uv, 1000); /* convert to mV */
|
||||
int low, high;
|
||||
int ret;
|
||||
|
||||
*selector = 0;
|
||||
|
||||
mutex_lock(&data->update_lock);
|
||||
low = pmbus_regulator_get_low_margin(client, s.page);
|
||||
if (low < 0)
|
||||
return low;
|
||||
if (low < 0) {
|
||||
ret = low;
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
high = pmbus_regulator_get_high_margin(client, s.page);
|
||||
if (high < 0)
|
||||
return high;
|
||||
if (high < 0) {
|
||||
ret = high;
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
/* Make sure we are within margins */
|
||||
if (low > val)
|
||||
|
|
@ -3217,7 +3288,10 @@ static int pmbus_regulator_set_voltage(struct regulator_dev *rdev, int min_uv,
|
|||
|
||||
val = pmbus_data2reg(data, &s, val);
|
||||
|
||||
return _pmbus_write_word_data(client, s.page, PMBUS_VOUT_COMMAND, (u16)val);
|
||||
ret = _pmbus_write_word_data(client, s.page, PMBUS_VOUT_COMMAND, (u16)val);
|
||||
unlock:
|
||||
mutex_unlock(&data->update_lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int pmbus_regulator_list_voltage(struct regulator_dev *rdev,
|
||||
|
|
@ -3227,6 +3301,7 @@ static int pmbus_regulator_list_voltage(struct regulator_dev *rdev,
|
|||
struct i2c_client *client = to_i2c_client(dev->parent);
|
||||
struct pmbus_data *data = i2c_get_clientdata(client);
|
||||
int val, low, high;
|
||||
int ret;
|
||||
|
||||
if (data->flags & PMBUS_VOUT_PROTECTED)
|
||||
return 0;
|
||||
|
|
@ -3239,18 +3314,29 @@ static int pmbus_regulator_list_voltage(struct regulator_dev *rdev,
|
|||
val = DIV_ROUND_CLOSEST(rdev->desc->min_uV +
|
||||
(rdev->desc->uV_step * selector), 1000); /* convert to mV */
|
||||
|
||||
mutex_lock(&data->update_lock);
|
||||
|
||||
low = pmbus_regulator_get_low_margin(client, rdev_get_id(rdev));
|
||||
if (low < 0)
|
||||
return low;
|
||||
if (low < 0) {
|
||||
ret = low;
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
high = pmbus_regulator_get_high_margin(client, rdev_get_id(rdev));
|
||||
if (high < 0)
|
||||
return high;
|
||||
if (high < 0) {
|
||||
ret = high;
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
if (val >= low && val <= high)
|
||||
return val * 1000; /* unit is uV */
|
||||
if (val >= low && val <= high) {
|
||||
ret = val * 1000; /* unit is uV */
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
return 0;
|
||||
ret = 0;
|
||||
unlock:
|
||||
mutex_unlock(&data->update_lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
const struct regulator_ops pmbus_regulator_ops = {
|
||||
|
|
@ -3281,12 +3367,42 @@ int pmbus_regulator_init_cb(struct regulator_dev *rdev,
|
|||
}
|
||||
EXPORT_SYMBOL_NS_GPL(pmbus_regulator_init_cb, "PMBUS");
|
||||
|
||||
static void pmbus_regulator_notify_work_cancel(void *data)
|
||||
{
|
||||
struct pmbus_data *pdata = data;
|
||||
|
||||
cancel_work_sync(&pdata->regulator_notify_work);
|
||||
}
|
||||
|
||||
static void pmbus_regulator_notify_worker(struct work_struct *work)
|
||||
{
|
||||
struct pmbus_data *data =
|
||||
container_of(work, struct pmbus_data, regulator_notify_work);
|
||||
int i, j;
|
||||
|
||||
for (i = 0; i < data->info->pages; i++) {
|
||||
int event;
|
||||
|
||||
event = atomic_xchg(&data->regulator_events[i], 0);
|
||||
if (!event)
|
||||
continue;
|
||||
|
||||
for (j = 0; j < data->info->num_regulators; j++) {
|
||||
if (i == rdev_get_id(data->rdevs[j])) {
|
||||
regulator_notifier_call_chain(data->rdevs[j],
|
||||
event, NULL);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int pmbus_regulator_register(struct pmbus_data *data)
|
||||
{
|
||||
struct device *dev = data->dev;
|
||||
const struct pmbus_driver_info *info = data->info;
|
||||
const struct pmbus_platform_data *pdata = dev_get_platdata(dev);
|
||||
int i;
|
||||
int i, ret;
|
||||
|
||||
data->rdevs = devm_kzalloc(dev, sizeof(struct regulator_dev *) * info->num_regulators,
|
||||
GFP_KERNEL);
|
||||
|
|
@ -3310,19 +3426,19 @@ static int pmbus_regulator_register(struct pmbus_data *data)
|
|||
info->reg_desc[i].name);
|
||||
}
|
||||
|
||||
INIT_WORK(&data->regulator_notify_work, pmbus_regulator_notify_worker);
|
||||
|
||||
ret = devm_add_action_or_reset(dev, pmbus_regulator_notify_work_cancel, data);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void pmbus_regulator_notify(struct pmbus_data *data, int page, int event)
|
||||
{
|
||||
int j;
|
||||
|
||||
for (j = 0; j < data->info->num_regulators; j++) {
|
||||
if (page == rdev_get_id(data->rdevs[j])) {
|
||||
regulator_notifier_call_chain(data->rdevs[j], event, NULL);
|
||||
break;
|
||||
}
|
||||
}
|
||||
atomic_or(event, &data->regulator_events[page]);
|
||||
schedule_work(&data->regulator_notify_work);
|
||||
}
|
||||
#else
|
||||
static int pmbus_regulator_register(struct pmbus_data *data)
|
||||
|
|
|
|||
Loading…
Reference in New Issue