From 1543e317f1da31b75942316931e8f491a8920811 Mon Sep 17 00:00:00 2001
From: hc <hc@nodka.com>
Date: Thu, 04 Jan 2024 10:08:02 +0000
Subject: [PATCH] disable FB
---
kernel/drivers/hwmon/pmbus/pmbus_core.c | 722 ++++++++++++++++++++++++++++++++++++------------------
1 files changed, 477 insertions(+), 245 deletions(-)
diff --git a/kernel/drivers/hwmon/pmbus/pmbus_core.c b/kernel/drivers/hwmon/pmbus/pmbus_core.c
index cd24b37..117e3ce 100644
--- a/kernel/drivers/hwmon/pmbus/pmbus_core.c
+++ b/kernel/drivers/hwmon/pmbus/pmbus_core.c
@@ -1,22 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Hardware monitoring driver for PMBus devices
*
* Copyright (c) 2010, 2011 Ericsson AB.
* Copyright (c) 2012 Guenter Roeck
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include <linux/debugfs.h>
@@ -29,7 +16,6 @@
#include <linux/i2c.h>
#include <linux/hwmon.h>
#include <linux/hwmon-sysfs.h>
-#include <linux/jiffies.h>
#include <linux/pmbus.h>
#include <linux/regulator/driver.h>
#include <linux/regulator/machine.h>
@@ -40,21 +26,6 @@
* with each call to krealloc
*/
#define PMBUS_ATTR_ALLOC_SIZE 32
-
-/*
- * Index into status register array, per status register group
- */
-#define PB_STATUS_BASE 0
-#define PB_STATUS_VOUT_BASE (PB_STATUS_BASE + PMBUS_PAGES)
-#define PB_STATUS_IOUT_BASE (PB_STATUS_VOUT_BASE + PMBUS_PAGES)
-#define PB_STATUS_FAN_BASE (PB_STATUS_IOUT_BASE + PMBUS_PAGES)
-#define PB_STATUS_FAN34_BASE (PB_STATUS_FAN_BASE + PMBUS_PAGES)
-#define PB_STATUS_TEMP_BASE (PB_STATUS_FAN34_BASE + PMBUS_PAGES)
-#define PB_STATUS_INPUT_BASE (PB_STATUS_TEMP_BASE + PMBUS_PAGES)
-#define PB_STATUS_VMON_BASE (PB_STATUS_INPUT_BASE + 1)
-
-#define PB_NUM_STATUS_REG (PB_STATUS_VMON_BASE + 1)
-
#define PMBUS_NAME_SIZE 24
struct pmbus_sensor {
@@ -62,6 +33,7 @@
char name[PMBUS_NAME_SIZE]; /* sysfs sensor name */
struct device_attribute attribute;
u8 page; /* page number */
+ u8 phase; /* phase number, 0xff for all phases */
u16 reg; /* register */
enum pmbus_sensor_classes class; /* sensor class */
bool update; /* runtime sensor update needed */
@@ -89,6 +61,21 @@
#define to_pmbus_label(_attr) \
container_of(_attr, struct pmbus_label, attribute)
+/* Macros for converting between sensor index and register/page/status mask */
+
+#define PB_STATUS_MASK 0xffff
+#define PB_REG_SHIFT 16
+#define PB_REG_MASK 0x3ff
+#define PB_PAGE_SHIFT 26
+#define PB_PAGE_MASK 0x3f
+
+#define pb_reg_to_index(page, reg, mask) (((page) << PB_PAGE_SHIFT) | \
+ ((reg) << PB_REG_SHIFT) | (mask))
+
+#define pb_index_to_page(index) (((index) >> PB_PAGE_SHIFT) & PB_PAGE_MASK)
+#define pb_index_to_reg(index) (((index) >> PB_REG_SHIFT) & PB_REG_MASK)
+#define pb_index_to_mask(index) ((index) & PB_STATUS_MASK)
+
struct pmbus_data {
struct device *dev;
struct device *hwmon_dev;
@@ -103,25 +90,18 @@
int max_attributes;
int num_attributes;
struct attribute_group group;
- const struct attribute_group *groups[2];
+ const struct attribute_group **groups;
struct dentry *debugfs; /* debugfs device directory */
struct pmbus_sensor *sensors;
struct mutex update_lock;
- bool valid;
- unsigned long last_updated; /* in jiffies */
-
- /*
- * A single status register covers multiple attributes,
- * so we keep them all together.
- */
- u16 status[PB_NUM_STATUS_REG];
bool has_status_word; /* device uses STATUS_WORD register */
int (*read_status)(struct i2c_client *client, int page);
- u8 currpage;
+ s16 currpage; /* current page, -1 for unknown/unset */
+ s16 currphase; /* current phase, 0xff for all, -1 for unknown/unset */
};
struct pmbus_debugfs_entry {
@@ -154,20 +134,23 @@
void pmbus_clear_cache(struct i2c_client *client)
{
struct pmbus_data *data = i2c_get_clientdata(client);
+ struct pmbus_sensor *sensor;
- data->valid = false;
+ for (sensor = data->sensors; sensor; sensor = sensor->next)
+ sensor->data = -ENODATA;
}
EXPORT_SYMBOL_GPL(pmbus_clear_cache);
-int pmbus_set_page(struct i2c_client *client, int page)
+int pmbus_set_page(struct i2c_client *client, int page, int phase)
{
struct pmbus_data *data = i2c_get_clientdata(client);
int rv;
- if (page < 0 || page == data->currpage)
+ if (page < 0)
return 0;
- if (!(data->info->func[page] & PMBUS_PAGE_VIRTUAL)) {
+ if (!(data->info->func[page] & PMBUS_PAGE_VIRTUAL) &&
+ data->info->pages > 1 && page != data->currpage) {
rv = i2c_smbus_write_byte_data(client, PMBUS_PAGE, page);
if (rv < 0)
return rv;
@@ -179,8 +162,16 @@
if (rv != page)
return -EIO;
}
-
data->currpage = page;
+
+ if (data->info->phases[page] && data->currphase != phase &&
+ !(data->info->func[page] & PMBUS_PHASE_VIRTUAL)) {
+ rv = i2c_smbus_write_byte_data(client, PMBUS_PHASE,
+ phase);
+ if (rv)
+ return rv;
+ }
+ data->currphase = phase;
return 0;
}
@@ -190,7 +181,7 @@
{
int rv;
- rv = pmbus_set_page(client, page);
+ rv = pmbus_set_page(client, page, 0xff);
if (rv < 0)
return rv;
@@ -221,7 +212,7 @@
{
int rv;
- rv = pmbus_set_page(client, page);
+ rv = pmbus_set_page(client, page, 0xff);
if (rv < 0)
return rv;
@@ -299,11 +290,11 @@
}
EXPORT_SYMBOL_GPL(pmbus_update_fan);
-int pmbus_read_word_data(struct i2c_client *client, int page, u8 reg)
+int pmbus_read_word_data(struct i2c_client *client, int page, int phase, u8 reg)
{
int rv;
- rv = pmbus_set_page(client, page);
+ rv = pmbus_set_page(client, page, phase);
if (rv < 0)
return rv;
@@ -333,14 +324,15 @@
* _pmbus_read_word_data() is similar to pmbus_read_word_data(), but checks if
* a device specific mapping function exists and calls it if necessary.
*/
-static int _pmbus_read_word_data(struct i2c_client *client, int page, int reg)
+static int _pmbus_read_word_data(struct i2c_client *client, int page,
+ int phase, int reg)
{
struct pmbus_data *data = i2c_get_clientdata(client);
const struct pmbus_driver_info *info = data->info;
int status;
if (info->read_word_data) {
- status = info->read_word_data(client, page, reg);
+ status = info->read_word_data(client, page, phase, reg);
if (status != -ENODATA)
return status;
}
@@ -348,14 +340,20 @@
if (reg >= PMBUS_VIRT_BASE)
return pmbus_read_virt_reg(client, page, reg);
- return pmbus_read_word_data(client, page, reg);
+ return pmbus_read_word_data(client, page, phase, reg);
+}
+
+/* Same as above, but without phase parameter, for use in check functions */
+static int __pmbus_read_word_data(struct i2c_client *client, int page, int reg)
+{
+ return _pmbus_read_word_data(client, page, 0xff, reg);
}
int pmbus_read_byte_data(struct i2c_client *client, int page, u8 reg)
{
int rv;
- rv = pmbus_set_page(client, page);
+ rv = pmbus_set_page(client, page, 0xff);
if (rv < 0)
return rv;
@@ -367,7 +365,7 @@
{
int rv;
- rv = pmbus_set_page(client, page);
+ rv = pmbus_set_page(client, page, 0xff);
if (rv < 0)
return rv;
@@ -453,7 +451,7 @@
have_rpm = !!(config & pmbus_fan_rpm_mask[id]);
if (want_rpm == have_rpm)
- return pmbus_read_word_data(client, page,
+ return pmbus_read_word_data(client, page, 0xff,
pmbus_fan_command_registers[id]);
/* Can't sensibly map between RPM and PWM, just return zero */
@@ -543,7 +541,7 @@
bool pmbus_check_word_register(struct i2c_client *client, int page, int reg)
{
- return pmbus_check_register(client, _pmbus_read_word_data, page, reg);
+ return pmbus_check_register(client, __pmbus_read_word_data, page, reg);
}
EXPORT_SYMBOL_GPL(pmbus_check_word_register);
@@ -555,79 +553,41 @@
}
EXPORT_SYMBOL_GPL(pmbus_get_driver_info);
-static struct _pmbus_status {
- u32 func;
- u16 base;
- u16 reg;
-} pmbus_status[] = {
- { PMBUS_HAVE_STATUS_VOUT, PB_STATUS_VOUT_BASE, PMBUS_STATUS_VOUT },
- { PMBUS_HAVE_STATUS_IOUT, PB_STATUS_IOUT_BASE, PMBUS_STATUS_IOUT },
- { PMBUS_HAVE_STATUS_TEMP, PB_STATUS_TEMP_BASE,
- PMBUS_STATUS_TEMPERATURE },
- { PMBUS_HAVE_STATUS_FAN12, PB_STATUS_FAN_BASE, PMBUS_STATUS_FAN_12 },
- { PMBUS_HAVE_STATUS_FAN34, PB_STATUS_FAN34_BASE, PMBUS_STATUS_FAN_34 },
-};
-
-static struct pmbus_data *pmbus_update_device(struct device *dev)
+static int pmbus_get_status(struct i2c_client *client, int page, int reg)
{
- struct i2c_client *client = to_i2c_client(dev->parent);
struct pmbus_data *data = i2c_get_clientdata(client);
- const struct pmbus_driver_info *info = data->info;
- struct pmbus_sensor *sensor;
+ int status;
- mutex_lock(&data->update_lock);
- if (time_after(jiffies, data->last_updated + HZ) || !data->valid) {
- int i, j;
-
- for (i = 0; i < info->pages; i++) {
- data->status[PB_STATUS_BASE + i]
- = data->read_status(client, i);
- for (j = 0; j < ARRAY_SIZE(pmbus_status); j++) {
- struct _pmbus_status *s = &pmbus_status[j];
-
- if (!(info->func[i] & s->func))
- continue;
- data->status[s->base + i]
- = _pmbus_read_byte_data(client, i,
- s->reg);
- }
- }
-
- if (info->func[0] & PMBUS_HAVE_STATUS_INPUT)
- data->status[PB_STATUS_INPUT_BASE]
- = _pmbus_read_byte_data(client, 0,
- PMBUS_STATUS_INPUT);
-
- if (info->func[0] & PMBUS_HAVE_STATUS_VMON)
- data->status[PB_STATUS_VMON_BASE]
- = _pmbus_read_byte_data(client, 0,
- PMBUS_VIRT_STATUS_VMON);
-
- for (sensor = data->sensors; sensor; sensor = sensor->next) {
- if (!data->valid || sensor->update)
- sensor->data
- = _pmbus_read_word_data(client,
- sensor->page,
- sensor->reg);
- }
- pmbus_clear_faults(client);
- data->last_updated = jiffies;
- data->valid = 1;
+ switch (reg) {
+ case PMBUS_STATUS_WORD:
+ status = data->read_status(client, page);
+ break;
+ default:
+ status = _pmbus_read_byte_data(client, page, reg);
+ break;
}
- mutex_unlock(&data->update_lock);
- return data;
+ if (status < 0)
+ pmbus_clear_faults(client);
+ return status;
+}
+
+static void pmbus_update_sensor_data(struct i2c_client *client, struct pmbus_sensor *sensor)
+{
+ if (sensor->data < 0 || sensor->update)
+ sensor->data = _pmbus_read_word_data(client, sensor->page,
+ sensor->phase, sensor->reg);
}
/*
* Convert linear sensor values to milli- or micro-units
* depending on sensor type.
*/
-static long pmbus_reg2data_linear(struct pmbus_data *data,
- struct pmbus_sensor *sensor)
+static s64 pmbus_reg2data_linear(struct pmbus_data *data,
+ struct pmbus_sensor *sensor)
{
s16 exponent;
s32 mantissa;
- long val;
+ s64 val;
if (sensor->class == PSC_VOLTAGE_OUT) { /* LINEAR16 */
exponent = data->exponent[sensor->page];
@@ -641,11 +601,11 @@
/* scale result to milli-units for all sensors except fans */
if (sensor->class != PSC_FAN)
- val = val * 1000L;
+ val = val * 1000LL;
/* scale result to micro-units for power sensors */
if (sensor->class == PSC_POWER)
- val = val * 1000L;
+ val = val * 1000LL;
if (exponent >= 0)
val <<= exponent;
@@ -659,8 +619,8 @@
* Convert direct sensor values to milli- or micro-units
* depending on sensor type.
*/
-static long pmbus_reg2data_direct(struct pmbus_data *data,
- struct pmbus_sensor *sensor)
+static s64 pmbus_reg2data_direct(struct pmbus_data *data,
+ struct pmbus_sensor *sensor)
{
s64 b, val = (s16)sensor->data;
s32 m, R;
@@ -696,20 +656,20 @@
}
val = div_s64(val - b, m);
- return clamp_val(val, LONG_MIN, LONG_MAX);
+ return val;
}
/*
* Convert VID sensor values to milli- or micro-units
* depending on sensor type.
*/
-static long pmbus_reg2data_vid(struct pmbus_data *data,
- struct pmbus_sensor *sensor)
+static s64 pmbus_reg2data_vid(struct pmbus_data *data,
+ struct pmbus_sensor *sensor)
{
long val = sensor->data;
long rv = 0;
- switch (data->info->vrm_version) {
+ switch (data->info->vrm_version[sensor->page]) {
case vr11:
if (val >= 0x02 && val <= 0xb2)
rv = DIV_ROUND_CLOSEST(160000 - (val - 2) * 625, 100);
@@ -722,13 +682,21 @@
if (val >= 0x01)
rv = 500 + (val - 1) * 10;
break;
+ case imvp9:
+ if (val >= 0x01)
+ rv = 200 + (val - 1) * 10;
+ break;
+ case amd625mv:
+ if (val >= 0x0 && val <= 0xd8)
+ rv = DIV_ROUND_CLOSEST(155000 - val * 625, 100);
+ break;
}
return rv;
}
-static long pmbus_reg2data(struct pmbus_data *data, struct pmbus_sensor *sensor)
+static s64 pmbus_reg2data(struct pmbus_data *data, struct pmbus_sensor *sensor)
{
- long val;
+ s64 val;
if (!sensor->convert)
return sensor->data;
@@ -752,7 +720,7 @@
#define MIN_MANTISSA (511 * 1000)
static u16 pmbus_data2reg_linear(struct pmbus_data *data,
- struct pmbus_sensor *sensor, long val)
+ struct pmbus_sensor *sensor, s64 val)
{
s16 exponent = 0, mantissa;
bool negative = false;
@@ -774,8 +742,8 @@
val <<= -data->exponent[sensor->page];
else
val >>= data->exponent[sensor->page];
- val = DIV_ROUND_CLOSEST(val, 1000);
- return val & 0xffff;
+ val = DIV_ROUND_CLOSEST_ULL(val, 1000);
+ return clamp_val(val, 0, 0xffff);
}
if (val < 0) {
@@ -785,14 +753,14 @@
/* Power is in uW. Convert to mW before converting. */
if (sensor->class == PSC_POWER)
- val = DIV_ROUND_CLOSEST(val, 1000L);
+ val = DIV_ROUND_CLOSEST_ULL(val, 1000);
/*
* For simplicity, convert fan data to milli-units
* before calculating the exponent.
*/
if (sensor->class == PSC_FAN)
- val = val * 1000;
+ val = val * 1000LL;
/* Reduce large mantissa until it fits into 10 bit */
while (val >= MAX_MANTISSA && exponent < 15) {
@@ -806,11 +774,7 @@
}
/* Convert mantissa from milli-units to units */
- mantissa = DIV_ROUND_CLOSEST(val, 1000);
-
- /* Ensure that resulting number is within range */
- if (mantissa > 0x3ff)
- mantissa = 0x3ff;
+ mantissa = clamp_val(DIV_ROUND_CLOSEST_ULL(val, 1000), 0, 0x3ff);
/* restore sign */
if (negative)
@@ -821,9 +785,9 @@
}
static u16 pmbus_data2reg_direct(struct pmbus_data *data,
- struct pmbus_sensor *sensor, long val)
+ struct pmbus_sensor *sensor, s64 val)
{
- s64 b, val64 = val;
+ s64 b;
s32 m, R;
m = data->info->m[sensor->class];
@@ -841,30 +805,30 @@
R -= 3; /* Adjust R and b for data in milli-units */
b *= 1000;
}
- val64 = val64 * m + b;
+ val = val * m + b;
while (R > 0) {
- val64 *= 10;
+ val *= 10;
R--;
}
while (R < 0) {
- val64 = div_s64(val64 + 5LL, 10L); /* round closest */
+ val = div_s64(val + 5LL, 10L); /* round closest */
R++;
}
- return (u16)clamp_val(val64, S16_MIN, S16_MAX);
+ return (u16)clamp_val(val, S16_MIN, S16_MAX);
}
static u16 pmbus_data2reg_vid(struct pmbus_data *data,
- struct pmbus_sensor *sensor, long val)
+ struct pmbus_sensor *sensor, s64 val)
{
val = clamp_val(val, 500, 1600);
- return 2 + DIV_ROUND_CLOSEST((1600 - val) * 100, 625);
+ return 2 + DIV_ROUND_CLOSEST_ULL((1600LL - val) * 100LL, 625);
}
static u16 pmbus_data2reg(struct pmbus_data *data,
- struct pmbus_sensor *sensor, long val)
+ struct pmbus_sensor *sensor, s64 val)
{
u16 regval;
@@ -909,38 +873,56 @@
* If a negative value is stored in any of the referenced registers, this value
* reflects an error code which will be returned.
*/
-static int pmbus_get_boolean(struct pmbus_data *data, struct pmbus_boolean *b,
+static int pmbus_get_boolean(struct i2c_client *client, struct pmbus_boolean *b,
int index)
{
+ struct pmbus_data *data = i2c_get_clientdata(client);
struct pmbus_sensor *s1 = b->s1;
struct pmbus_sensor *s2 = b->s2;
- u16 reg = (index >> 16) & 0xffff;
- u16 mask = index & 0xffff;
+ u16 mask = pb_index_to_mask(index);
+ u8 page = pb_index_to_page(index);
+ u16 reg = pb_index_to_reg(index);
int ret, status;
u16 regval;
- status = data->status[reg];
- if (status < 0)
- return status;
+ mutex_lock(&data->update_lock);
+ status = pmbus_get_status(client, page, reg);
+ if (status < 0) {
+ ret = status;
+ goto unlock;
+ }
+
+ if (s1)
+ pmbus_update_sensor_data(client, s1);
+ if (s2)
+ pmbus_update_sensor_data(client, s2);
regval = status & mask;
- if (!s1 && !s2) {
- ret = !!regval;
- } else if (!s1 || !s2) {
- WARN(1, "Bad boolean descriptor %p: s1=%p, s2=%p\n", b, s1, s2);
- return 0;
- } else {
- long v1, v2;
+ if (regval) {
+ ret = pmbus_write_byte_data(client, page, reg, regval);
+ if (ret)
+ goto unlock;
+ }
+ if (s1 && s2) {
+ s64 v1, v2;
- if (s1->data < 0)
- return s1->data;
- if (s2->data < 0)
- return s2->data;
+ if (s1->data < 0) {
+ ret = s1->data;
+ goto unlock;
+ }
+ if (s2->data < 0) {
+ ret = s2->data;
+ goto unlock;
+ }
v1 = pmbus_reg2data(data, s1);
v2 = pmbus_reg2data(data, s2);
ret = !!(regval && v1 >= v2);
+ } else {
+ ret = !!regval;
}
+unlock:
+ mutex_unlock(&data->update_lock);
return ret;
}
@@ -949,10 +931,10 @@
{
struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
struct pmbus_boolean *boolean = to_pmbus_boolean(attr);
- struct pmbus_data *data = pmbus_update_device(dev);
+ struct i2c_client *client = to_i2c_client(dev->parent);
int val;
- val = pmbus_get_boolean(data, boolean, attr->index);
+ val = pmbus_get_boolean(client, boolean, attr->index);
if (val < 0)
return val;
return snprintf(buf, PAGE_SIZE, "%d\n", val);
@@ -961,13 +943,19 @@
static ssize_t pmbus_show_sensor(struct device *dev,
struct device_attribute *devattr, char *buf)
{
- struct pmbus_data *data = pmbus_update_device(dev);
+ struct i2c_client *client = to_i2c_client(dev->parent);
struct pmbus_sensor *sensor = to_pmbus_sensor(devattr);
+ struct pmbus_data *data = i2c_get_clientdata(client);
+ ssize_t ret;
+ mutex_lock(&data->update_lock);
+ pmbus_update_sensor_data(client, sensor);
if (sensor->data < 0)
- return sensor->data;
-
- return snprintf(buf, PAGE_SIZE, "%ld\n", pmbus_reg2data(data, sensor));
+ ret = sensor->data;
+ else
+ ret = snprintf(buf, PAGE_SIZE, "%lld\n", pmbus_reg2data(data, sensor));
+ mutex_unlock(&data->update_lock);
+ return ret;
}
static ssize_t pmbus_set_sensor(struct device *dev,
@@ -978,11 +966,11 @@
struct pmbus_data *data = i2c_get_clientdata(client);
struct pmbus_sensor *sensor = to_pmbus_sensor(devattr);
ssize_t rv = count;
- long val = 0;
+ s64 val;
int ret;
u16 regval;
- if (kstrtol(buf, 10, &val) < 0)
+ if (kstrtos64(buf, 10, &val) < 0)
return -EINVAL;
mutex_lock(&data->update_lock);
@@ -1008,9 +996,9 @@
{
if (data->num_attributes >= data->max_attributes - 1) {
int new_max_attrs = data->max_attributes + PMBUS_ATTR_ALLOC_SIZE;
- void *new_attrs = krealloc(data->group.attrs,
- new_max_attrs * sizeof(void *),
- GFP_KERNEL);
+ void *new_attrs = devm_krealloc(data->dev, data->group.attrs,
+ new_max_attrs * sizeof(void *),
+ GFP_KERNEL);
if (!new_attrs)
return -ENOMEM;
data->group.attrs = new_attrs;
@@ -1058,10 +1046,13 @@
const char *name, const char *type, int seq,
struct pmbus_sensor *s1,
struct pmbus_sensor *s2,
- u16 reg, u16 mask)
+ u8 page, u16 reg, u16 mask)
{
struct pmbus_boolean *boolean;
struct sensor_device_attribute *a;
+
+ if (WARN((s1 && !s2) || (!s1 && s2), "Bad s1/s2 parameters\n"))
+ return -EINVAL;
boolean = devm_kzalloc(data->dev, sizeof(*boolean), GFP_KERNEL);
if (!boolean)
@@ -1073,15 +1064,16 @@
name, seq, type);
boolean->s1 = s1;
boolean->s2 = s2;
- pmbus_attr_init(a, boolean->name, S_IRUGO, pmbus_show_boolean, NULL,
- (reg << 16) | mask);
+ pmbus_attr_init(a, boolean->name, 0444, pmbus_show_boolean, NULL,
+ pb_reg_to_index(page, reg, mask));
return pmbus_add_attribute(data, &a->dev_attr.attr);
}
static struct pmbus_sensor *pmbus_add_sensor(struct pmbus_data *data,
const char *name, const char *type,
- int seq, int page, int reg,
+ int seq, int page, int phase,
+ int reg,
enum pmbus_sensor_classes class,
bool update, bool readonly,
bool convert)
@@ -1101,13 +1093,18 @@
snprintf(sensor->name, sizeof(sensor->name), "%s%d",
name, seq);
+ if (data->flags & PMBUS_WRITE_PROTECTED)
+ readonly = true;
+
sensor->page = page;
+ sensor->phase = phase;
sensor->reg = reg;
sensor->class = class;
sensor->update = update;
sensor->convert = convert;
+ sensor->data = -ENODATA;
pmbus_dev_attr_init(a, sensor->name,
- readonly ? S_IRUGO : S_IRUGO | S_IWUSR,
+ readonly ? 0444 : 0644,
pmbus_show_sensor, pmbus_set_sensor);
if (pmbus_add_attribute(data, &a->attr))
@@ -1121,7 +1118,7 @@
static int pmbus_add_label(struct pmbus_data *data,
const char *name, int seq,
- const char *lstring, int index)
+ const char *lstring, int index, int phase)
{
struct pmbus_label *label;
struct device_attribute *a;
@@ -1133,13 +1130,23 @@
a = &label->attribute;
snprintf(label->name, sizeof(label->name), "%s%d_label", name, seq);
- if (!index)
- strncpy(label->label, lstring, sizeof(label->label) - 1);
- else
- snprintf(label->label, sizeof(label->label), "%s%d", lstring,
- index);
+ if (!index) {
+ if (phase == 0xff)
+ strncpy(label->label, lstring,
+ sizeof(label->label) - 1);
+ else
+ snprintf(label->label, sizeof(label->label), "%s.%d",
+ lstring, phase);
+ } else {
+ if (phase == 0xff)
+ snprintf(label->label, sizeof(label->label), "%s%d",
+ lstring, index);
+ else
+ snprintf(label->label, sizeof(label->label), "%s%d.%d",
+ lstring, index, phase);
+ }
- pmbus_dev_attr_init(a, label->name, S_IRUGO, pmbus_show_label, NULL);
+ pmbus_dev_attr_init(a, label->name, 0444, pmbus_show_label, NULL);
return pmbus_add_attribute(data, &a->attr);
}
@@ -1176,7 +1183,7 @@
bool compare; /* true if compare function needed */
u32 func; /* sensor mask */
u32 sfunc; /* sensor status mask */
- int sbase; /* status base register */
+ int sreg; /* status register */
const struct pmbus_limit_attr *limit;/* limit registers */
};
@@ -1202,7 +1209,7 @@
for (i = 0; i < nlimit; i++) {
if (pmbus_check_word_register(client, page, l->reg)) {
curr = pmbus_add_sensor(data, name, l->attr, index,
- page, l->reg, attr->class,
+ page, 0xff, l->reg, attr->class,
attr->update || l->update,
false, true);
if (!curr)
@@ -1214,7 +1221,7 @@
: NULL,
attr->compare ? l->low ? base : curr
: NULL,
- attr->sbase + page, l->sbit);
+ page, attr->sreg, l->sbit);
if (ret)
return ret;
have_alarm = 1;
@@ -1229,7 +1236,7 @@
struct pmbus_data *data,
const struct pmbus_driver_info *info,
const char *name,
- int index, int page,
+ int index, int page, int phase,
const struct pmbus_sensor_attr *attr,
bool paged)
{
@@ -1239,15 +1246,16 @@
if (attr->label) {
ret = pmbus_add_label(data, name, index, attr->label,
- paged ? page + 1 : 0);
+ paged ? page + 1 : 0, phase);
if (ret)
return ret;
}
- base = pmbus_add_sensor(data, name, "input", index, page, attr->reg,
- attr->class, true, true, true);
+ base = pmbus_add_sensor(data, name, "input", index, page, phase,
+ attr->reg, attr->class, true, true, true);
if (!base)
return -ENOMEM;
- if (attr->sfunc) {
+ /* No limit and alarm attributes for phase specific sensors */
+ if (attr->sfunc && phase == 0xff) {
ret = pmbus_add_limit_attrs(client, data, info, name,
index, page, base, attr);
if (ret < 0)
@@ -1263,7 +1271,7 @@
pmbus_check_status_register(client, page)) {
ret = pmbus_add_boolean(data, name, "alarm", index,
NULL, NULL,
- PB_STATUS_BASE + page,
+ page, PMBUS_STATUS_WORD,
attr->gbit);
if (ret)
return ret;
@@ -1317,10 +1325,25 @@
continue;
ret = pmbus_add_sensor_attrs_one(client, data, info,
name, index, page,
- attrs, paged);
+ 0xff, attrs, paged);
if (ret)
return ret;
index++;
+ if (info->phases[page]) {
+ int phase;
+
+ for (phase = 0; phase < info->phases[page];
+ phase++) {
+ if (!(info->pfunc[phase] & attrs->func))
+ continue;
+ ret = pmbus_add_sensor_attrs_one(client,
+ data, info, name, index, page,
+ phase, attrs, paged);
+ if (ret)
+ return ret;
+ index++;
+ }
+ }
}
attrs++;
}
@@ -1337,7 +1360,7 @@
.reg = PMBUS_VIN_UV_FAULT_LIMIT,
.attr = "lcrit",
.alarm = "lcrit_alarm",
- .sbit = PB_VOLTAGE_UV_FAULT,
+ .sbit = PB_VOLTAGE_UV_FAULT | PB_VOLTAGE_VIN_OFF,
}, {
.reg = PMBUS_VIN_OV_WARN_LIMIT,
.attr = "max",
@@ -1363,6 +1386,12 @@
}, {
.reg = PMBUS_VIRT_RESET_VIN_HISTORY,
.attr = "reset_history",
+ }, {
+ .reg = PMBUS_MFR_VIN_MIN,
+ .attr = "rated_min",
+ }, {
+ .reg = PMBUS_MFR_VIN_MAX,
+ .attr = "rated_max",
},
};
@@ -1426,7 +1455,13 @@
}, {
.reg = PMBUS_VIRT_RESET_VOUT_HISTORY,
.attr = "reset_history",
- }
+ }, {
+ .reg = PMBUS_MFR_VOUT_MIN,
+ .attr = "rated_min",
+ }, {
+ .reg = PMBUS_MFR_VOUT_MAX,
+ .attr = "rated_max",
+ },
};
static const struct pmbus_sensor_attr voltage_attributes[] = {
@@ -1436,7 +1471,7 @@
.label = "vin",
.func = PMBUS_HAVE_VIN,
.sfunc = PMBUS_HAVE_STATUS_INPUT,
- .sbase = PB_STATUS_INPUT_BASE,
+ .sreg = PMBUS_STATUS_INPUT,
.gbit = PB_STATUS_VIN_UV,
.limit = vin_limit_attrs,
.nlimit = ARRAY_SIZE(vin_limit_attrs),
@@ -1446,7 +1481,7 @@
.label = "vmon",
.func = PMBUS_HAVE_VMON,
.sfunc = PMBUS_HAVE_STATUS_VMON,
- .sbase = PB_STATUS_VMON_BASE,
+ .sreg = PMBUS_VIRT_STATUS_VMON,
.limit = vmon_limit_attrs,
.nlimit = ARRAY_SIZE(vmon_limit_attrs),
}, {
@@ -1461,7 +1496,7 @@
.paged = true,
.func = PMBUS_HAVE_VOUT,
.sfunc = PMBUS_HAVE_STATUS_VOUT,
- .sbase = PB_STATUS_VOUT_BASE,
+ .sreg = PMBUS_STATUS_VOUT,
.gbit = PB_STATUS_VOUT_OV,
.limit = vout_limit_attrs,
.nlimit = ARRAY_SIZE(vout_limit_attrs),
@@ -1496,7 +1531,10 @@
}, {
.reg = PMBUS_VIRT_RESET_IIN_HISTORY,
.attr = "reset_history",
- }
+ }, {
+ .reg = PMBUS_MFR_IIN_MAX,
+ .attr = "rated_max",
+ },
};
static const struct pmbus_limit_attr iout_limit_attrs[] = {
@@ -1530,7 +1568,10 @@
}, {
.reg = PMBUS_VIRT_RESET_IOUT_HISTORY,
.attr = "reset_history",
- }
+ }, {
+ .reg = PMBUS_MFR_IOUT_MAX,
+ .attr = "rated_max",
+ },
};
static const struct pmbus_sensor_attr current_attributes[] = {
@@ -1540,7 +1581,7 @@
.label = "iin",
.func = PMBUS_HAVE_IIN,
.sfunc = PMBUS_HAVE_STATUS_INPUT,
- .sbase = PB_STATUS_INPUT_BASE,
+ .sreg = PMBUS_STATUS_INPUT,
.gbit = PB_STATUS_INPUT,
.limit = iin_limit_attrs,
.nlimit = ARRAY_SIZE(iin_limit_attrs),
@@ -1551,7 +1592,7 @@
.paged = true,
.func = PMBUS_HAVE_IOUT,
.sfunc = PMBUS_HAVE_STATUS_IOUT,
- .sbase = PB_STATUS_IOUT_BASE,
+ .sreg = PMBUS_STATUS_IOUT,
.gbit = PB_STATUS_IOUT_OC,
.limit = iout_limit_attrs,
.nlimit = ARRAY_SIZE(iout_limit_attrs),
@@ -1581,7 +1622,10 @@
}, {
.reg = PMBUS_VIRT_RESET_PIN_HISTORY,
.attr = "reset_history",
- }
+ }, {
+ .reg = PMBUS_MFR_PIN_MAX,
+ .attr = "rated_max",
+ },
};
static const struct pmbus_limit_attr pout_limit_attrs[] = {
@@ -1615,7 +1659,10 @@
}, {
.reg = PMBUS_VIRT_RESET_POUT_HISTORY,
.attr = "reset_history",
- }
+ }, {
+ .reg = PMBUS_MFR_POUT_MAX,
+ .attr = "rated_max",
+ },
};
static const struct pmbus_sensor_attr power_attributes[] = {
@@ -1625,7 +1672,7 @@
.label = "pin",
.func = PMBUS_HAVE_PIN,
.sfunc = PMBUS_HAVE_STATUS_INPUT,
- .sbase = PB_STATUS_INPUT_BASE,
+ .sreg = PMBUS_STATUS_INPUT,
.gbit = PB_STATUS_INPUT,
.limit = pin_limit_attrs,
.nlimit = ARRAY_SIZE(pin_limit_attrs),
@@ -1636,7 +1683,7 @@
.paged = true,
.func = PMBUS_HAVE_POUT,
.sfunc = PMBUS_HAVE_STATUS_IOUT,
- .sbase = PB_STATUS_IOUT_BASE,
+ .sreg = PMBUS_STATUS_IOUT,
.limit = pout_limit_attrs,
.nlimit = ARRAY_SIZE(pout_limit_attrs),
}
@@ -1679,7 +1726,10 @@
}, {
.reg = PMBUS_VIRT_RESET_TEMP_HISTORY,
.attr = "reset_history",
- }
+ }, {
+ .reg = PMBUS_MFR_MAX_TEMP_1,
+ .attr = "rated_max",
+ },
};
static const struct pmbus_limit_attr temp_limit_attrs2[] = {
@@ -1717,7 +1767,10 @@
}, {
.reg = PMBUS_VIRT_RESET_TEMP2_HISTORY,
.attr = "reset_history",
- }
+ }, {
+ .reg = PMBUS_MFR_MAX_TEMP_2,
+ .attr = "rated_max",
+ },
};
static const struct pmbus_limit_attr temp_limit_attrs3[] = {
@@ -1743,7 +1796,10 @@
.attr = "crit",
.alarm = "crit_alarm",
.sbit = PB_TEMP_OT_FAULT,
- }
+ }, {
+ .reg = PMBUS_MFR_MAX_TEMP_3,
+ .attr = "rated_max",
+ },
};
static const struct pmbus_sensor_attr temp_attributes[] = {
@@ -1755,7 +1811,7 @@
.compare = true,
.func = PMBUS_HAVE_TEMP,
.sfunc = PMBUS_HAVE_STATUS_TEMP,
- .sbase = PB_STATUS_TEMP_BASE,
+ .sreg = PMBUS_STATUS_TEMPERATURE,
.gbit = PB_STATUS_TEMPERATURE,
.limit = temp_limit_attrs,
.nlimit = ARRAY_SIZE(temp_limit_attrs),
@@ -1767,7 +1823,7 @@
.compare = true,
.func = PMBUS_HAVE_TEMP2,
.sfunc = PMBUS_HAVE_STATUS_TEMP,
- .sbase = PB_STATUS_TEMP_BASE,
+ .sreg = PMBUS_STATUS_TEMPERATURE,
.gbit = PB_STATUS_TEMPERATURE,
.limit = temp_limit_attrs2,
.nlimit = ARRAY_SIZE(temp_limit_attrs2),
@@ -1779,7 +1835,7 @@
.compare = true,
.func = PMBUS_HAVE_TEMP3,
.sfunc = PMBUS_HAVE_STATUS_TEMP,
- .sbase = PB_STATUS_TEMP_BASE,
+ .sreg = PMBUS_STATUS_TEMPERATURE,
.gbit = PB_STATUS_TEMPERATURE,
.limit = temp_limit_attrs3,
.nlimit = ARRAY_SIZE(temp_limit_attrs3),
@@ -1824,7 +1880,7 @@
struct pmbus_sensor *sensor;
sensor = pmbus_add_sensor(data, "fan", "target", index, page,
- PMBUS_VIRT_FAN_TARGET_1 + id, PSC_FAN,
+ 0xff, PMBUS_VIRT_FAN_TARGET_1 + id, PSC_FAN,
false, false, true);
if (!sensor)
@@ -1835,14 +1891,14 @@
return 0;
sensor = pmbus_add_sensor(data, "pwm", NULL, index, page,
- PMBUS_VIRT_PWM_1 + id, PSC_PWM,
+ 0xff, PMBUS_VIRT_PWM_1 + id, PSC_PWM,
false, false, true);
if (!sensor)
return -ENOMEM;
sensor = pmbus_add_sensor(data, "pwm", "enable", index, page,
- PMBUS_VIRT_PWM_ENABLE_1 + id, PSC_PWM,
+ 0xff, PMBUS_VIRT_PWM_ENABLE_1 + id, PSC_PWM,
true, false, false);
if (!sensor)
@@ -1884,7 +1940,7 @@
continue;
if (pmbus_add_sensor(data, "fan", "input", index,
- page, pmbus_fan_registers[f],
+ page, 0xff, pmbus_fan_registers[f],
PSC_FAN, true, true, true) == NULL)
return -ENOMEM;
@@ -1904,19 +1960,19 @@
if ((info->func[page] & pmbus_fan_status_flags[f]) &&
pmbus_check_byte_register(client,
page, pmbus_fan_status_registers[f])) {
- int base;
+ int reg;
if (f > 1) /* fan 3, 4 */
- base = PB_STATUS_FAN34_BASE + page;
+ reg = PMBUS_STATUS_FAN_34;
else
- base = PB_STATUS_FAN_BASE + page;
+ reg = PMBUS_STATUS_FAN_12;
ret = pmbus_add_boolean(data, "fan",
- "alarm", index, NULL, NULL, base,
+ "alarm", index, NULL, NULL, page, reg,
PB_FAN_FAN1_WARNING >> (f & 1));
if (ret)
return ret;
ret = pmbus_add_boolean(data, "fan",
- "fault", index, NULL, NULL, base,
+ "fault", index, NULL, NULL, page, reg,
PB_FAN_FAN1_FAULT >> (f & 1));
if (ret)
return ret;
@@ -1924,6 +1980,118 @@
index++;
}
}
+ return 0;
+}
+
+struct pmbus_samples_attr {
+ int reg;
+ char *name;
+};
+
+struct pmbus_samples_reg {
+ int page;
+ struct pmbus_samples_attr *attr;
+ struct device_attribute dev_attr;
+};
+
+static struct pmbus_samples_attr pmbus_samples_registers[] = {
+ {
+ .reg = PMBUS_VIRT_SAMPLES,
+ .name = "samples",
+ }, {
+ .reg = PMBUS_VIRT_IN_SAMPLES,
+ .name = "in_samples",
+ }, {
+ .reg = PMBUS_VIRT_CURR_SAMPLES,
+ .name = "curr_samples",
+ }, {
+ .reg = PMBUS_VIRT_POWER_SAMPLES,
+ .name = "power_samples",
+ }, {
+ .reg = PMBUS_VIRT_TEMP_SAMPLES,
+ .name = "temp_samples",
+ }
+};
+
+#define to_samples_reg(x) container_of(x, struct pmbus_samples_reg, dev_attr)
+
+static ssize_t pmbus_show_samples(struct device *dev,
+ struct device_attribute *devattr, char *buf)
+{
+ int val;
+ struct i2c_client *client = to_i2c_client(dev->parent);
+ struct pmbus_samples_reg *reg = to_samples_reg(devattr);
+ struct pmbus_data *data = i2c_get_clientdata(client);
+
+ mutex_lock(&data->update_lock);
+ val = _pmbus_read_word_data(client, reg->page, 0xff, reg->attr->reg);
+ mutex_unlock(&data->update_lock);
+ if (val < 0)
+ return val;
+
+ return snprintf(buf, PAGE_SIZE, "%d\n", val);
+}
+
+static ssize_t pmbus_set_samples(struct device *dev,
+ struct device_attribute *devattr,
+ const char *buf, size_t count)
+{
+ int ret;
+ long val;
+ struct i2c_client *client = to_i2c_client(dev->parent);
+ struct pmbus_samples_reg *reg = to_samples_reg(devattr);
+ struct pmbus_data *data = i2c_get_clientdata(client);
+
+ if (kstrtol(buf, 0, &val) < 0)
+ return -EINVAL;
+
+ mutex_lock(&data->update_lock);
+ ret = _pmbus_write_word_data(client, reg->page, reg->attr->reg, val);
+ mutex_unlock(&data->update_lock);
+
+ return ret ? : count;
+}
+
+static int pmbus_add_samples_attr(struct pmbus_data *data, int page,
+ struct pmbus_samples_attr *attr)
+{
+ struct pmbus_samples_reg *reg;
+
+ reg = devm_kzalloc(data->dev, sizeof(*reg), GFP_KERNEL);
+ if (!reg)
+ return -ENOMEM;
+
+ reg->attr = attr;
+ reg->page = page;
+
+ pmbus_dev_attr_init(®->dev_attr, attr->name, 0644,
+ pmbus_show_samples, pmbus_set_samples);
+
+ return pmbus_add_attribute(data, ®->dev_attr.attr);
+}
+
+static int pmbus_add_samples_attributes(struct i2c_client *client,
+ struct pmbus_data *data)
+{
+ const struct pmbus_driver_info *info = data->info;
+ int s;
+
+ if (!(info->func[0] & PMBUS_HAVE_SAMPLES))
+ return 0;
+
+ for (s = 0; s < ARRAY_SIZE(pmbus_samples_registers); s++) {
+ struct pmbus_samples_attr *attr;
+ int ret;
+
+ attr = &pmbus_samples_registers[s];
+ if (!pmbus_check_word_register(client, 0, attr->reg))
+ continue;
+
+ ret = pmbus_add_samples_attr(data, 0, attr);
+ if (ret)
+ return ret;
+ }
+
return 0;
}
@@ -1958,6 +2126,10 @@
/* Fans */
ret = pmbus_add_fan_attributes(client, data);
+ if (ret)
+ return ret;
+
+ ret = pmbus_add_samples_attributes(client, data);
return ret;
}
@@ -2009,7 +2181,7 @@
static int pmbus_read_status_word(struct i2c_client *client, int page)
{
- return _pmbus_read_word_data(client, page, PMBUS_STATUS_WORD);
+ return _pmbus_read_word_data(client, page, 0xff, PMBUS_STATUS_WORD);
}
static int pmbus_init_common(struct i2c_client *client, struct pmbus_data *data,
@@ -2040,6 +2212,15 @@
ret = i2c_smbus_read_byte_data(client, PMBUS_CAPABILITY);
if (ret >= 0 && (ret & PB_CAPABILITY_ERROR_CHECK))
client->flags |= I2C_CLIENT_PEC;
+
+ /*
+ * Check if the chip is write protected. If it is, we can not clear
+ * faults, and we should not try it. Also, in that case, writes into
+ * limit registers need to be disabled.
+ */
+ ret = i2c_smbus_read_byte_data(client, PMBUS_WRITE_PROTECT);
+ if (ret > 0 && (ret & PB_WP_ANY))
+ data->flags |= PMBUS_WRITE_PROTECTED | PMBUS_SKIP_STATUS_CHECK;
if (data->info->pages)
pmbus_clear_faults(client);
@@ -2074,10 +2255,14 @@
{
struct device *dev = rdev_get_dev(rdev);
struct i2c_client *client = to_i2c_client(dev->parent);
+ struct pmbus_data *data = i2c_get_clientdata(client);
u8 page = rdev_get_id(rdev);
int ret;
+ mutex_lock(&data->update_lock);
ret = pmbus_read_byte_data(client, page, PMBUS_OPERATION);
+ mutex_unlock(&data->update_lock);
+
if (ret < 0)
return ret;
@@ -2088,11 +2273,17 @@
{
struct device *dev = rdev_get_dev(rdev);
struct i2c_client *client = to_i2c_client(dev->parent);
+ struct pmbus_data *data = i2c_get_clientdata(client);
u8 page = rdev_get_id(rdev);
+ int ret;
- return pmbus_update_byte_data(client, page, PMBUS_OPERATION,
- PB_OPERATION_CONTROL_ON,
- enable ? PB_OPERATION_CONTROL_ON : 0);
+ mutex_lock(&data->update_lock);
+ ret = pmbus_update_byte_data(client, page, PMBUS_OPERATION,
+ PB_OPERATION_CONTROL_ON,
+ enable ? PB_OPERATION_CONTROL_ON : 0);
+ mutex_unlock(&data->update_lock);
+
+ return ret;
}
static int pmbus_regulator_enable(struct regulator_dev *rdev)
@@ -2183,6 +2374,42 @@
DEFINE_DEBUGFS_ATTRIBUTE(pmbus_debugfs_ops_status, pmbus_debugfs_get_status,
NULL, "0x%04llx\n");
+static int pmbus_debugfs_get_pec(void *data, u64 *val)
+{
+ struct i2c_client *client = data;
+
+ *val = !!(client->flags & I2C_CLIENT_PEC);
+
+ return 0;
+}
+
+static int pmbus_debugfs_set_pec(void *data, u64 val)
+{
+ int rc;
+ struct i2c_client *client = data;
+
+ if (!val) {
+ client->flags &= ~I2C_CLIENT_PEC;
+ return 0;
+ }
+
+ if (val != 1)
+ return -EINVAL;
+
+ rc = i2c_smbus_read_byte_data(client, PMBUS_CAPABILITY);
+ if (rc < 0)
+ return rc;
+
+ if (!(rc & PB_CAPABILITY_ERROR_CHECK))
+ return -EOPNOTSUPP;
+
+ client->flags |= I2C_CLIENT_PEC;
+
+ return 0;
+}
+DEFINE_DEBUGFS_ATTRIBUTE(pmbus_debugfs_ops_pec, pmbus_debugfs_get_pec,
+ pmbus_debugfs_set_pec, "%llu\n");
+
static int pmbus_init_debugfs(struct i2c_client *client,
struct pmbus_data *data)
{
@@ -2210,6 +2437,9 @@
GFP_KERNEL);
if (!entries)
return -ENOMEM;
+
+ debugfs_create_file("pec", 0664, data->debugfs, client,
+ &pmbus_debugfs_ops_pec);
for (i = 0; i < data->info->pages; ++i) {
/* Check accessibility of status register if it's not page 0 */
@@ -2325,12 +2555,12 @@
}
#endif /* IS_ENABLED(CONFIG_DEBUG_FS) */
-int pmbus_do_probe(struct i2c_client *client, const struct i2c_device_id *id,
- struct pmbus_driver_info *info)
+int pmbus_do_probe(struct i2c_client *client, struct pmbus_driver_info *info)
{
struct device *dev = &client->dev;
const struct pmbus_platform_data *pdata = dev_get_platdata(dev);
struct pmbus_data *data;
+ size_t groups_num = 0;
int ret;
if (!info)
@@ -2345,6 +2575,15 @@
if (!data)
return -ENOMEM;
+ if (info->groups)
+ while (info->groups[groups_num])
+ groups_num++;
+
+ data->groups = devm_kcalloc(dev, groups_num + 2, sizeof(void *),
+ GFP_KERNEL);
+ if (!data->groups)
+ return -ENOMEM;
+
i2c_set_clientdata(client, data);
mutex_init(&data->update_lock);
data->dev = dev;
@@ -2352,6 +2591,8 @@
if (pdata)
data->flags = pdata->flags;
data->info = info;
+ data->currpage = -1;
+ data->currphase = -1;
ret = pmbus_init_common(client, data, info);
if (ret < 0)
@@ -2359,7 +2600,7 @@
ret = pmbus_find_attributes(client, data);
if (ret)
- goto out_kfree;
+ return ret;
/*
* If there are no attributes, something is wrong.
@@ -2367,34 +2608,27 @@
*/
if (!data->num_attributes) {
dev_err(dev, "No attributes found\n");
- ret = -ENODEV;
- goto out_kfree;
+ return -ENODEV;
}
data->groups[0] = &data->group;
- data->hwmon_dev = hwmon_device_register_with_groups(dev, client->name,
- data, data->groups);
+ memcpy(data->groups + 1, info->groups, sizeof(void *) * groups_num);
+ data->hwmon_dev = devm_hwmon_device_register_with_groups(dev,
+ client->name, data, data->groups);
if (IS_ERR(data->hwmon_dev)) {
- ret = PTR_ERR(data->hwmon_dev);
dev_err(dev, "Failed to register hwmon device\n");
- goto out_kfree;
+ return PTR_ERR(data->hwmon_dev);
}
ret = pmbus_regulator_register(data);
if (ret)
- goto out_unregister;
+ return ret;
ret = pmbus_init_debugfs(client, data);
if (ret)
dev_warn(dev, "Failed to register debugfs\n");
return 0;
-
-out_unregister:
- hwmon_device_unregister(data->hwmon_dev);
-out_kfree:
- kfree(data->group.attrs);
- return ret;
}
EXPORT_SYMBOL_GPL(pmbus_do_probe);
@@ -2404,8 +2638,6 @@
debugfs_remove_recursive(data->debugfs);
- hwmon_device_unregister(data->hwmon_dev);
- kfree(data->group.attrs);
return 0;
}
EXPORT_SYMBOL_GPL(pmbus_do_remove);
--
Gitblit v1.6.2