2023-02-07 12:02:59

by Naresh Solanki

[permalink] [raw]
Subject: [PATCH v2 1/4] hwmon: (pmbus/core): Generalize pmbus status flag map

The PMBus status flag map(pmbus_regulator_status_flag_map) is moved
outside of the regulator #if block and the associated variable/struct
name updated to reflect as generic PMBus status.

This will make the PMBus status flag map more versatile and easier to
incorporate into different contexts and functions.

Signed-off-by: Naresh Solanki <[email protected]>
---
drivers/hwmon/pmbus/pmbus_core.c | 94 ++++++++++++++++----------------
1 file changed, 47 insertions(+), 47 deletions(-)

diff --git a/drivers/hwmon/pmbus/pmbus_core.c b/drivers/hwmon/pmbus/pmbus_core.c
index 95e95783972a..1b70cf3be313 100644
--- a/drivers/hwmon/pmbus/pmbus_core.c
+++ b/drivers/hwmon/pmbus/pmbus_core.c
@@ -2692,6 +2692,49 @@ static int pmbus_init_common(struct i2c_client *client, struct pmbus_data *data,
return 0;
}

+/* A PMBus status flag and the corresponding REGULATOR_ERROR_* flag */
+struct pmbus_status_assoc {
+ int pflag, rflag;
+};
+
+/* PMBus->regulator bit mappings for a PMBus status register */
+struct pmbus_status_category {
+ int func;
+ int reg;
+ const struct pmbus_status_assoc *bits; /* zero-terminated */
+};
+
+static const struct pmbus_status_category __maybe_unused pmbus_status_flag_map[] = {
+ {
+ .func = PMBUS_HAVE_STATUS_VOUT,
+ .reg = PMBUS_STATUS_VOUT,
+ .bits = (const struct pmbus_status_assoc[]) {
+ { PB_VOLTAGE_UV_WARNING, REGULATOR_ERROR_UNDER_VOLTAGE_WARN },
+ { PB_VOLTAGE_UV_FAULT, REGULATOR_ERROR_UNDER_VOLTAGE },
+ { PB_VOLTAGE_OV_WARNING, REGULATOR_ERROR_OVER_VOLTAGE_WARN },
+ { PB_VOLTAGE_OV_FAULT, REGULATOR_ERROR_REGULATION_OUT },
+ { },
+ },
+ }, {
+ .func = PMBUS_HAVE_STATUS_IOUT,
+ .reg = PMBUS_STATUS_IOUT,
+ .bits = (const struct pmbus_status_assoc[]) {
+ { PB_IOUT_OC_WARNING, REGULATOR_ERROR_OVER_CURRENT_WARN },
+ { PB_IOUT_OC_FAULT, REGULATOR_ERROR_OVER_CURRENT },
+ { PB_IOUT_OC_LV_FAULT, REGULATOR_ERROR_OVER_CURRENT },
+ { },
+ },
+ }, {
+ .func = PMBUS_HAVE_STATUS_TEMP,
+ .reg = PMBUS_STATUS_TEMPERATURE,
+ .bits = (const struct pmbus_status_assoc[]) {
+ { PB_TEMP_OT_WARNING, REGULATOR_ERROR_OVER_TEMP_WARN },
+ { PB_TEMP_OT_FAULT, REGULATOR_ERROR_OVER_TEMP },
+ { },
+ },
+ },
+};
+
#if IS_ENABLED(CONFIG_REGULATOR)
static int pmbus_regulator_is_enabled(struct regulator_dev *rdev)
{
@@ -2738,54 +2781,11 @@ static int pmbus_regulator_disable(struct regulator_dev *rdev)
return _pmbus_regulator_on_off(rdev, 0);
}

-/* A PMBus status flag and the corresponding REGULATOR_ERROR_* flag */
-struct pmbus_regulator_status_assoc {
- int pflag, rflag;
-};
-
-/* PMBus->regulator bit mappings for a PMBus status register */
-struct pmbus_regulator_status_category {
- int func;
- int reg;
- const struct pmbus_regulator_status_assoc *bits; /* zero-terminated */
-};
-
-static const struct pmbus_regulator_status_category pmbus_regulator_flag_map[] = {
- {
- .func = PMBUS_HAVE_STATUS_VOUT,
- .reg = PMBUS_STATUS_VOUT,
- .bits = (const struct pmbus_regulator_status_assoc[]) {
- { PB_VOLTAGE_UV_WARNING, REGULATOR_ERROR_UNDER_VOLTAGE_WARN },
- { PB_VOLTAGE_UV_FAULT, REGULATOR_ERROR_UNDER_VOLTAGE },
- { PB_VOLTAGE_OV_WARNING, REGULATOR_ERROR_OVER_VOLTAGE_WARN },
- { PB_VOLTAGE_OV_FAULT, REGULATOR_ERROR_REGULATION_OUT },
- { },
- },
- }, {
- .func = PMBUS_HAVE_STATUS_IOUT,
- .reg = PMBUS_STATUS_IOUT,
- .bits = (const struct pmbus_regulator_status_assoc[]) {
- { PB_IOUT_OC_WARNING, REGULATOR_ERROR_OVER_CURRENT_WARN },
- { PB_IOUT_OC_FAULT, REGULATOR_ERROR_OVER_CURRENT },
- { PB_IOUT_OC_LV_FAULT, REGULATOR_ERROR_OVER_CURRENT },
- { },
- },
- }, {
- .func = PMBUS_HAVE_STATUS_TEMP,
- .reg = PMBUS_STATUS_TEMPERATURE,
- .bits = (const struct pmbus_regulator_status_assoc[]) {
- { PB_TEMP_OT_WARNING, REGULATOR_ERROR_OVER_TEMP_WARN },
- { PB_TEMP_OT_FAULT, REGULATOR_ERROR_OVER_TEMP },
- { },
- },
- },
-};
-
static int pmbus_regulator_get_error_flags(struct regulator_dev *rdev, unsigned int *flags)
{
int i, status;
- const struct pmbus_regulator_status_category *cat;
- const struct pmbus_regulator_status_assoc *bit;
+ const struct pmbus_status_category *cat;
+ const struct pmbus_status_assoc *bit;
struct device *dev = rdev_get_dev(rdev);
struct i2c_client *client = to_i2c_client(dev->parent);
struct pmbus_data *data = i2c_get_clientdata(client);
@@ -2796,8 +2796,8 @@ static int pmbus_regulator_get_error_flags(struct regulator_dev *rdev, unsigned

mutex_lock(&data->update_lock);

- for (i = 0; i < ARRAY_SIZE(pmbus_regulator_flag_map); i++) {
- cat = &pmbus_regulator_flag_map[i];
+ for (i = 0; i < ARRAY_SIZE(pmbus_status_flag_map); i++) {
+ cat = &pmbus_status_flag_map[i];
if (!(func & cat->func))
continue;


base-commit: 7505dab78f58c953b863753208eeca682e8126ff
--
2.39.1



2023-02-07 12:03:02

by Naresh Solanki

[permalink] [raw]
Subject: [PATCH v2 2/4] hwmon: (pmbus/core) Generalise pmbus get status

Add function pmbus get status that can be used to get both pmbus
specific status & regulator status

Signed-off-by: Naresh Solanki <[email protected]>
...
Changes in V2:
- Add __maybe_unused attribute for pmbus_get_status function
- Remove unrelated changes
---
drivers/hwmon/pmbus/pmbus_core.c | 118 +++++++++++++++++--------------
1 file changed, 66 insertions(+), 52 deletions(-)

diff --git a/drivers/hwmon/pmbus/pmbus_core.c b/drivers/hwmon/pmbus/pmbus_core.c
index 1b70cf3be313..5ccae8126a56 100644
--- a/drivers/hwmon/pmbus/pmbus_core.c
+++ b/drivers/hwmon/pmbus/pmbus_core.c
@@ -2735,61 +2735,14 @@ static const struct pmbus_status_category __maybe_unused pmbus_status_flag_map[]
},
};

-#if IS_ENABLED(CONFIG_REGULATOR)
-static int pmbus_regulator_is_enabled(struct regulator_dev *rdev)
-{
- 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;
-
- return !!(ret & PB_OPERATION_CONTROL_ON);
-}
-
-static int _pmbus_regulator_on_off(struct regulator_dev *rdev, bool enable)
-{
- 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_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)
-{
- return _pmbus_regulator_on_off(rdev, 1);
-}
-
-static int pmbus_regulator_disable(struct regulator_dev *rdev)
-{
- return _pmbus_regulator_on_off(rdev, 0);
-}

-static int pmbus_regulator_get_error_flags(struct regulator_dev *rdev, unsigned int *flags)
+static int __maybe_unused pmbus_get_flags(struct pmbus_data *data, u8 page, unsigned int *flags)
{
- int i, status;
+ int i, status, ret;
const struct pmbus_status_category *cat;
const struct pmbus_status_assoc *bit;
- 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);
+ struct device *dev = data->dev;
+ struct i2c_client *client = to_i2c_client(dev);
int func = data->info->func[page];

*flags = 0;
@@ -2827,7 +2780,13 @@ static int pmbus_regulator_get_error_flags(struct regulator_dev *rdev, unsigned
if (status < 0)
return status;

- if (pmbus_regulator_is_enabled(rdev)) {
+ mutex_lock(&data->update_lock);
+ ret = _pmbus_read_byte_data(client, page, PMBUS_OPERATION);
+ mutex_unlock(&data->update_lock);
+ if (ret < 0)
+ return status;
+
+ if (ret & PB_OPERATION_CONTROL_ON) {
if (status & PB_STATUS_OFF)
*flags |= REGULATOR_ERROR_FAIL;

@@ -2855,6 +2814,61 @@ static int pmbus_regulator_get_error_flags(struct regulator_dev *rdev, unsigned
return 0;
}

+#if IS_ENABLED(CONFIG_REGULATOR)
+static int pmbus_regulator_is_enabled(struct regulator_dev *rdev)
+{
+ 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;
+
+ return !!(ret & PB_OPERATION_CONTROL_ON);
+}
+
+static int _pmbus_regulator_on_off(struct regulator_dev *rdev, bool enable)
+{
+ 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_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)
+{
+ return _pmbus_regulator_on_off(rdev, 1);
+}
+
+static int pmbus_regulator_disable(struct regulator_dev *rdev)
+{
+ return _pmbus_regulator_on_off(rdev, 0);
+}
+
+static int pmbus_regulator_get_error_flags(struct regulator_dev *rdev, unsigned int *flags)
+{
+ struct device *dev = rdev_get_dev(rdev);
+ struct i2c_client *client = to_i2c_client(dev->parent);
+ struct pmbus_data *data = i2c_get_clientdata(client);
+
+ return pmbus_get_flags(data, rdev_get_id(rdev), flags);
+}
+
static int pmbus_regulator_get_status(struct regulator_dev *rdev)
{
struct device *dev = rdev_get_dev(rdev);
--
2.39.1


2023-02-07 12:03:05

by Naresh Solanki

[permalink] [raw]
Subject: [PATCH v2 3/4] hwmon: (pmbus/core): Add interrupt support

From: Patrick Rudolph <[email protected]>

Implement PMBUS irq handler.

Signed-off-by: Patrick Rudolph <[email protected]>
Signed-off-by: Naresh Solanki <[email protected]>
---
drivers/hwmon/pmbus/pmbus.h | 2 +-
drivers/hwmon/pmbus/pmbus_core.c | 85 ++++++++++++++++++++++++++++++++
2 files changed, 86 insertions(+), 1 deletion(-)

diff --git a/drivers/hwmon/pmbus/pmbus.h b/drivers/hwmon/pmbus/pmbus.h
index 713ea7915425..11e84e141126 100644
--- a/drivers/hwmon/pmbus/pmbus.h
+++ b/drivers/hwmon/pmbus/pmbus.h
@@ -26,7 +26,7 @@ enum pmbus_regs {

PMBUS_CAPABILITY = 0x19,
PMBUS_QUERY = 0x1A,
-
+ PMBUS_SMBALERT_MASK = 0x1B,
PMBUS_VOUT_MODE = 0x20,
PMBUS_VOUT_COMMAND = 0x21,
PMBUS_VOUT_TRIM = 0x22,
diff --git a/drivers/hwmon/pmbus/pmbus_core.c b/drivers/hwmon/pmbus/pmbus_core.c
index 5ccae8126a56..d5403baad60a 100644
--- a/drivers/hwmon/pmbus/pmbus_core.c
+++ b/drivers/hwmon/pmbus/pmbus_core.c
@@ -3093,6 +3093,85 @@ static int pmbus_regulator_register(struct pmbus_data *data)
}
#endif

+static int pmbus_write_smbalert_mask(struct i2c_client *client, u8 page, u8 reg, u8 val)
+{
+ int err;
+
+ err = pmbus_check_word_register(client, page, reg | (val << 8));
+ if (err)
+ return err;
+
+ return pmbus_write_word_data(client, page, PMBUS_SMBALERT_MASK, reg | (val << 8));
+}
+
+static irqreturn_t pmbus_fault_handler(int irq, void *pdata)
+{
+ struct pmbus_data *data = pdata;
+ struct i2c_client *client = to_i2c_client(data->dev);
+ int i, status;
+
+ mutex_lock(&data->update_lock);
+ for (i = 0; i < data->info->pages; i++) {
+ status = pmbus_read_status_word(client, i);
+ if (status < 0) {
+ mutex_unlock(&data->update_lock);
+ return status;
+ }
+
+ if (status & ~(PB_STATUS_OFF | PB_STATUS_BUSY | PB_STATUS_POWER_GOOD_N))
+ pmbus_clear_fault_page(client, i);
+ }
+ mutex_unlock(&data->update_lock);
+
+ return IRQ_HANDLED;
+}
+
+static int pmbus_irq_setup(struct i2c_client *client, struct pmbus_data *data)
+{
+ struct device *dev = &client->dev;
+ const struct pmbus_status_category *cat;
+ const struct pmbus_status_assoc *bit;
+ int i, j, err, ret, func;
+ u8 mask;
+ u8 misc_status[] = {PMBUS_STATUS_CML, PMBUS_STATUS_OTHER, PMBUS_STATUS_MFR_SPECIFIC,
+ PMBUS_STATUS_FAN_12, PMBUS_STATUS_FAN_34};
+
+ for (i = 0; i < data->info->pages; i++) {
+ func = data->info->func[i];
+
+ for (j = 0; j < ARRAY_SIZE(pmbus_status_flag_map); j++) {
+ cat = &pmbus_status_flag_map[j];
+ if (!(func & cat->func))
+ continue;
+ mask = 0;
+ for (bit = cat->bits; bit->pflag; bit++)
+ mask |= bit->pflag;
+
+ err = pmbus_write_smbalert_mask(client, i, cat->reg, ~mask);
+ if (err)
+ dev_err_once(dev, "Failed to set smbalert for reg 0x%02x\n",
+ cat->reg);
+ }
+
+ for (j = 0; j < ARRAY_SIZE(misc_status); j++) {
+ err = pmbus_write_smbalert_mask(client, i, misc_status[j], 0xff);
+ if (err)
+ dev_err_once(dev, "Failed to set smbalert for reg 0x%02x\n",
+ misc_status[j]);
+ }
+ }
+
+ /* Register notifiers - can fail if IRQ is not given */
+ ret = devm_request_threaded_irq(dev, client->irq, NULL, pmbus_fault_handler, 0,
+ "pmbus-irq", data);
+ if (ret) {
+ dev_warn(dev, "IRQ disabled %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
static struct dentry *pmbus_debugfs_dir; /* pmbus debugfs directory */

#if IS_ENABLED(CONFIG_DEBUG_FS)
@@ -3455,6 +3534,12 @@ int pmbus_do_probe(struct i2c_client *client, struct pmbus_driver_info *info)
if (ret)
return ret;

+ if (client->irq > 0) {
+ ret = pmbus_irq_setup(client, data);
+ if (ret)
+ return ret;
+ }
+
ret = pmbus_init_debugfs(client, data);
if (ret)
dev_warn(dev, "Failed to register debugfs\n");
--
2.39.1


2023-02-07 12:03:07

by Naresh Solanki

[permalink] [raw]
Subject: [PATCH v2 4/4] hwmon: (pmbus/core): Notify hwmon events

From: Patrick Rudolph <[email protected]>

Notify hwmon events using the pmbus irq handler.

Signed-off-by: Patrick Rudolph <[email protected]>
Signed-off-by: Naresh Solanki <[email protected]>
...
Changes in V2
- Remove __maybe_unsed attribute as its not needed.
---
drivers/hwmon/pmbus/pmbus_core.c | 48 ++++++++++++++++++++++++++++----
1 file changed, 43 insertions(+), 5 deletions(-)

diff --git a/drivers/hwmon/pmbus/pmbus_core.c b/drivers/hwmon/pmbus/pmbus_core.c
index d5403baad60a..f6778a9c7126 100644
--- a/drivers/hwmon/pmbus/pmbus_core.c
+++ b/drivers/hwmon/pmbus/pmbus_core.c
@@ -2735,8 +2735,36 @@ static const struct pmbus_status_category __maybe_unused pmbus_status_flag_map[]
},
};

+#define to_dev_attr(_dev_attr) \
+ container_of(_dev_attr, struct device_attribute, attr)
+
+static void pmbus_notify(struct pmbus_data *data, int page, int reg, int flags)
+{
+ int i;
+
+ for (i = 0; i < data->num_attributes; i++) {
+ struct device_attribute *da = to_dev_attr(data->group.attrs[i]);
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
+ int index = attr->index;
+ u16 smask = pb_index_to_mask(index);
+ u8 spage = pb_index_to_page(index);
+ u16 sreg = pb_index_to_reg(index);
+
+ if (reg == sreg && page == spage && (smask & flags)) {
+ dev_dbg(data->dev, "sysfs notify: %s", da->attr.name);
+ sysfs_notify(&data->dev->kobj, NULL, da->attr.name);
+ kobject_uevent(&data->dev->kobj, KOBJ_CHANGE);
+ flags &= ~smask;
+ }
+
+ if (!flags)
+ break;
+ }
+}
+
+static int pmbus_get_flags(struct pmbus_data *data, u8 page, unsigned int *flags,
+ bool notify)

-static int __maybe_unused pmbus_get_flags(struct pmbus_data *data, u8 page, unsigned int *flags)
{
int i, status, ret;
const struct pmbus_status_category *cat;
@@ -2764,6 +2792,10 @@ static int __maybe_unused pmbus_get_flags(struct pmbus_data *data, u8 page, unsi
if (status & bit->pflag)
*flags |= bit->rflag;
}
+
+ if (notify && status)
+ pmbus_notify(data, page, cat->reg, status);
+
}

/*
@@ -2866,7 +2898,7 @@ static int pmbus_regulator_get_error_flags(struct regulator_dev *rdev, unsigned
struct i2c_client *client = to_i2c_client(dev->parent);
struct pmbus_data *data = i2c_get_clientdata(client);

- return pmbus_get_flags(data, rdev_get_id(rdev), flags);
+ return pmbus_get_flags(data, rdev_get_id(rdev), flags, false);
}

static int pmbus_regulator_get_status(struct regulator_dev *rdev)
@@ -3108,10 +3140,14 @@ static irqreturn_t pmbus_fault_handler(int irq, void *pdata)
{
struct pmbus_data *data = pdata;
struct i2c_client *client = to_i2c_client(data->dev);
- int i, status;
+ int i, status, ret;

- mutex_lock(&data->update_lock);
for (i = 0; i < data->info->pages; i++) {
+ ret = pmbus_get_flags(data, i, &status, true);
+ if (ret)
+ return ret;
+
+ mutex_lock(&data->update_lock);
status = pmbus_read_status_word(client, i);
if (status < 0) {
mutex_unlock(&data->update_lock);
@@ -3120,8 +3156,10 @@ static irqreturn_t pmbus_fault_handler(int irq, void *pdata)

if (status & ~(PB_STATUS_OFF | PB_STATUS_BUSY | PB_STATUS_POWER_GOOD_N))
pmbus_clear_fault_page(client, i);
+
+ mutex_unlock(&data->update_lock);
}
- mutex_unlock(&data->update_lock);
+

return IRQ_HANDLED;
}
--
2.39.1


2023-02-11 15:07:57

by Guenter Roeck

[permalink] [raw]
Subject: Re: [PATCH v2 2/4] hwmon: (pmbus/core) Generalise pmbus get status

On Tue, Feb 07, 2023 at 01:02:39PM +0100, Naresh Solanki wrote:
> Add function pmbus get status that can be used to get both pmbus
> specific status & regulator status
>
> Signed-off-by: Naresh Solanki <[email protected]>
> ...
> Changes in V2:
> - Add __maybe_unused attribute for pmbus_get_status function
> - Remove unrelated changes
> ---
> drivers/hwmon/pmbus/pmbus_core.c | 118 +++++++++++++++++--------------
> 1 file changed, 66 insertions(+), 52 deletions(-)
>
> diff --git a/drivers/hwmon/pmbus/pmbus_core.c b/drivers/hwmon/pmbus/pmbus_core.c
> index 1b70cf3be313..5ccae8126a56 100644
> --- a/drivers/hwmon/pmbus/pmbus_core.c
> +++ b/drivers/hwmon/pmbus/pmbus_core.c
> @@ -2735,61 +2735,14 @@ static const struct pmbus_status_category __maybe_unused pmbus_status_flag_map[]
> },
> };
>
> -#if IS_ENABLED(CONFIG_REGULATOR)
> -static int pmbus_regulator_is_enabled(struct regulator_dev *rdev)
> -{
> - 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;
> -
> - return !!(ret & PB_OPERATION_CONTROL_ON);
> -}
> -
> -static int _pmbus_regulator_on_off(struct regulator_dev *rdev, bool enable)
> -{
> - 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_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)
> -{
> - return _pmbus_regulator_on_off(rdev, 1);
> -}
> -
> -static int pmbus_regulator_disable(struct regulator_dev *rdev)
> -{
> - return _pmbus_regulator_on_off(rdev, 0);
> -}
>
> -static int pmbus_regulator_get_error_flags(struct regulator_dev *rdev, unsigned int *flags)
> +static int __maybe_unused pmbus_get_flags(struct pmbus_data *data, u8 page, unsigned int *flags)
> {
> - int i, status;
> + int i, status, ret;
> const struct pmbus_status_category *cat;
> const struct pmbus_status_assoc *bit;
> - 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);
> + struct device *dev = data->dev;
> + struct i2c_client *client = to_i2c_client(dev);
> int func = data->info->func[page];
>
> *flags = 0;
> @@ -2827,7 +2780,13 @@ static int pmbus_regulator_get_error_flags(struct regulator_dev *rdev, unsigned
> if (status < 0)
> return status;
>
> - if (pmbus_regulator_is_enabled(rdev)) {
> + mutex_lock(&data->update_lock);
> + ret = _pmbus_read_byte_data(client, page, PMBUS_OPERATION);
> + mutex_unlock(&data->update_lock);
> + if (ret < 0)
> + return status;
> +
> + if (ret & PB_OPERATION_CONTROL_ON) {

This code is now executed twice. Please keep the original function,
just rename it to pmbus_is_enabled() or similar, then call it from
pmbus_regulator_is_enabled() which then just needs to adjust the
parameter (and maybe pass 'dev' as argument).

> if (status & PB_STATUS_OFF)
> *flags |= REGULATOR_ERROR_FAIL;
>
> @@ -2855,6 +2814,61 @@ static int pmbus_regulator_get_error_flags(struct regulator_dev *rdev, unsigned
> return 0;
> }
>
> +#if IS_ENABLED(CONFIG_REGULATOR)
> +static int pmbus_regulator_is_enabled(struct regulator_dev *rdev)
> +{
> + 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;
> +
> + return !!(ret & PB_OPERATION_CONTROL_ON);

This could then be as simple as

return pmbus_is_enabled(rdev_get_dev(rdev));

Thanks,
Guenter

> +}
> +
> +static int _pmbus_regulator_on_off(struct regulator_dev *rdev, bool enable)
> +{
> + 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_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)
> +{
> + return _pmbus_regulator_on_off(rdev, 1);
> +}
> +
> +static int pmbus_regulator_disable(struct regulator_dev *rdev)
> +{
> + return _pmbus_regulator_on_off(rdev, 0);
> +}
> +
> +static int pmbus_regulator_get_error_flags(struct regulator_dev *rdev, unsigned int *flags)
> +{
> + struct device *dev = rdev_get_dev(rdev);
> + struct i2c_client *client = to_i2c_client(dev->parent);
> + struct pmbus_data *data = i2c_get_clientdata(client);
> +
> + return pmbus_get_flags(data, rdev_get_id(rdev), flags);
> +}
> +
> static int pmbus_regulator_get_status(struct regulator_dev *rdev)
> {
> struct device *dev = rdev_get_dev(rdev);

2023-02-11 15:37:36

by Guenter Roeck

[permalink] [raw]
Subject: Re: [PATCH v2 3/4] hwmon: (pmbus/core): Add interrupt support

On Tue, Feb 07, 2023 at 01:02:40PM +0100, Naresh Solanki wrote:
> From: Patrick Rudolph <[email protected]>
>
> Implement PMBUS irq handler.
>
> Signed-off-by: Patrick Rudolph <[email protected]>
> Signed-off-by: Naresh Solanki <[email protected]>
> ---
> drivers/hwmon/pmbus/pmbus.h | 2 +-
> drivers/hwmon/pmbus/pmbus_core.c | 85 ++++++++++++++++++++++++++++++++
> 2 files changed, 86 insertions(+), 1 deletion(-)
>
> diff --git a/drivers/hwmon/pmbus/pmbus.h b/drivers/hwmon/pmbus/pmbus.h
> index 713ea7915425..11e84e141126 100644
> --- a/drivers/hwmon/pmbus/pmbus.h
> +++ b/drivers/hwmon/pmbus/pmbus.h
> @@ -26,7 +26,7 @@ enum pmbus_regs {
>
> PMBUS_CAPABILITY = 0x19,
> PMBUS_QUERY = 0x1A,
> -
> + PMBUS_SMBALERT_MASK = 0x1B,
> PMBUS_VOUT_MODE = 0x20,
> PMBUS_VOUT_COMMAND = 0x21,
> PMBUS_VOUT_TRIM = 0x22,
> diff --git a/drivers/hwmon/pmbus/pmbus_core.c b/drivers/hwmon/pmbus/pmbus_core.c
> index 5ccae8126a56..d5403baad60a 100644
> --- a/drivers/hwmon/pmbus/pmbus_core.c
> +++ b/drivers/hwmon/pmbus/pmbus_core.c
> @@ -3093,6 +3093,85 @@ static int pmbus_regulator_register(struct pmbus_data *data)
> }
> #endif
>
> +static int pmbus_write_smbalert_mask(struct i2c_client *client, u8 page, u8 reg, u8 val)
> +{
> + int err;
> +
> + err = pmbus_check_word_register(client, page, reg | (val << 8));
> + if (err)
> + return err;

I am not convinced that this is necessary. The next command will return an
error anyway if the register or the specific mask is not supported, so what
is the point ?

> +
> + return pmbus_write_word_data(client, page, PMBUS_SMBALERT_MASK, reg | (val << 8));
> +}
> +
> +static irqreturn_t pmbus_fault_handler(int irq, void *pdata)
> +{
> + struct pmbus_data *data = pdata;
> + struct i2c_client *client = to_i2c_client(data->dev);
> + int i, status;
> +
> + mutex_lock(&data->update_lock);
> + for (i = 0; i < data->info->pages; i++) {
> + status = pmbus_read_status_word(client, i);
> + if (status < 0) {
> + mutex_unlock(&data->update_lock);
> + return status;
> + }
> +
> + if (status & ~(PB_STATUS_OFF | PB_STATUS_BUSY | PB_STATUS_POWER_GOOD_N))
> + pmbus_clear_fault_page(client, i);
> + }
> + mutex_unlock(&data->update_lock);
> +
> + return IRQ_HANDLED;
> +}
> +
> +static int pmbus_irq_setup(struct i2c_client *client, struct pmbus_data *data)
> +{
> + struct device *dev = &client->dev;
> + const struct pmbus_status_category *cat;
> + const struct pmbus_status_assoc *bit;
> + int i, j, err, ret, func;
> + u8 mask;
> + u8 misc_status[] = {PMBUS_STATUS_CML, PMBUS_STATUS_OTHER, PMBUS_STATUS_MFR_SPECIFIC,
> + PMBUS_STATUS_FAN_12, PMBUS_STATUS_FAN_34};

static const u8 ...

> +
> + for (i = 0; i < data->info->pages; i++) {
> + func = data->info->func[i];
> +
> + for (j = 0; j < ARRAY_SIZE(pmbus_status_flag_map); j++) {
> + cat = &pmbus_status_flag_map[j];
> + if (!(func & cat->func))
> + continue;
> + mask = 0;
> + for (bit = cat->bits; bit->pflag; bit++)
> + mask |= bit->pflag;
> +
> + err = pmbus_write_smbalert_mask(client, i, cat->reg, ~mask);
> + if (err)
> + dev_err_once(dev, "Failed to set smbalert for reg 0x%02x\n",
> + cat->reg);

dev_err implies an error. This is ignored and thus not an error. On top of that,
not all chips support PMBUS_SMBALERT_MASK. All of those would see this message.
We can't have that. At best make it a dev_dbg.

> + }
> +
> + for (j = 0; j < ARRAY_SIZE(misc_status); j++) {
> + err = pmbus_write_smbalert_mask(client, i, misc_status[j], 0xff);
> + if (err)
> + dev_err_once(dev, "Failed to set smbalert for reg 0x%02x\n",
> + misc_status[j]);

We definitely can't have a message here; that would fire for almost
every chip.

> + }
> + }
> +
> + /* Register notifiers - can fail if IRQ is not given */

If there is no irq, what is the point of executing this code in the first
place ? No, wait, in that case the function isn't called in the first place.
I think the comment doesn't add any value and is just confusing.

> + ret = devm_request_threaded_irq(dev, client->irq, NULL, pmbus_fault_handler, 0,
> + "pmbus-irq", data);
> + if (ret) {

Why both "err" and "ret" ?

> + dev_warn(dev, "IRQ disabled %d\n", ret);

The calling code aborts, so this should be dev_err() and say something
better than "IRQ disabled"; It should be something like "failed to
request irq".

> + return ret;
> + }
> +
> + return 0;
> +}
> +
> static struct dentry *pmbus_debugfs_dir; /* pmbus debugfs directory */
>
> #if IS_ENABLED(CONFIG_DEBUG_FS)
> @@ -3455,6 +3534,12 @@ int pmbus_do_probe(struct i2c_client *client, struct pmbus_driver_info *info)
> if (ret)
> return ret;
>
> + if (client->irq > 0) {
> + ret = pmbus_irq_setup(client, data);
> + if (ret)
> + return ret;
> + }
> +

I think it would be better to have the check in pmbus_irq_setup():

pmbus_irq_setup()
{
if (!client->irq)
return 0;

...
}

and here
ret = pmbus_irq_setup(client, data);
if (ret)
return ret;


> ret = pmbus_init_debugfs(client, data);
> if (ret)
> dev_warn(dev, "Failed to register debugfs\n");

2023-02-11 15:46:54

by Guenter Roeck

[permalink] [raw]
Subject: Re: [PATCH v2 4/4] hwmon: (pmbus/core): Notify hwmon events

On Tue, Feb 07, 2023 at 01:02:41PM +0100, Naresh Solanki wrote:
> From: Patrick Rudolph <[email protected]>
>
> Notify hwmon events using the pmbus irq handler.
>
> Signed-off-by: Patrick Rudolph <[email protected]>
> Signed-off-by: Naresh Solanki <[email protected]>
> ...
> Changes in V2
> - Remove __maybe_unsed attribute as its not needed.
> ---
> drivers/hwmon/pmbus/pmbus_core.c | 48 ++++++++++++++++++++++++++++----
> 1 file changed, 43 insertions(+), 5 deletions(-)
>
> diff --git a/drivers/hwmon/pmbus/pmbus_core.c b/drivers/hwmon/pmbus/pmbus_core.c
> index d5403baad60a..f6778a9c7126 100644
> --- a/drivers/hwmon/pmbus/pmbus_core.c
> +++ b/drivers/hwmon/pmbus/pmbus_core.c
> @@ -2735,8 +2735,36 @@ static const struct pmbus_status_category __maybe_unused pmbus_status_flag_map[]
> },
> };
>
> +#define to_dev_attr(_dev_attr) \
> + container_of(_dev_attr, struct device_attribute, attr)
> +
> +static void pmbus_notify(struct pmbus_data *data, int page, int reg, int flags)
> +{
> + int i;
> +
> + for (i = 0; i < data->num_attributes; i++) {
> + struct device_attribute *da = to_dev_attr(data->group.attrs[i]);
> + struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
> + int index = attr->index;
> + u16 smask = pb_index_to_mask(index);
> + u8 spage = pb_index_to_page(index);
> + u16 sreg = pb_index_to_reg(index);
> +
> + if (reg == sreg && page == spage && (smask & flags)) {
> + dev_dbg(data->dev, "sysfs notify: %s", da->attr.name);
> + sysfs_notify(&data->dev->kobj, NULL, da->attr.name);
> + kobject_uevent(&data->dev->kobj, KOBJ_CHANGE);
> + flags &= ~smask;
> + }
> +
> + if (!flags)
> + break;
> + }
> +}
> +
> +static int pmbus_get_flags(struct pmbus_data *data, u8 page, unsigned int *flags,
> + bool notify)
>
> -static int __maybe_unused pmbus_get_flags(struct pmbus_data *data, u8 page, unsigned int *flags)
> {
> int i, status, ret;
> const struct pmbus_status_category *cat;
> @@ -2764,6 +2792,10 @@ static int __maybe_unused pmbus_get_flags(struct pmbus_data *data, u8 page, unsi
> if (status & bit->pflag)
> *flags |= bit->rflag;
> }
> +
> + if (notify && status)
> + pmbus_notify(data, page, cat->reg, status);
> +
> }
>
> /*
> @@ -2866,7 +2898,7 @@ static int pmbus_regulator_get_error_flags(struct regulator_dev *rdev, unsigned
> struct i2c_client *client = to_i2c_client(dev->parent);
> struct pmbus_data *data = i2c_get_clientdata(client);
>
> - return pmbus_get_flags(data, rdev_get_id(rdev), flags);
> + return pmbus_get_flags(data, rdev_get_id(rdev), flags, false);
> }
>
> static int pmbus_regulator_get_status(struct regulator_dev *rdev)
> @@ -3108,10 +3140,14 @@ static irqreturn_t pmbus_fault_handler(int irq, void *pdata)
> {
> struct pmbus_data *data = pdata;
> struct i2c_client *client = to_i2c_client(data->dev);
> - int i, status;
> + int i, status, ret;
>
> - mutex_lock(&data->update_lock);
> for (i = 0; i < data->info->pages; i++) {
> + ret = pmbus_get_flags(data, i, &status, true);
> + if (ret)
> + return ret;
> +
> + mutex_lock(&data->update_lock);

You should introduce a locked version of pmbus_get_flags() and call
that function, and keep the existing locking in place.

> status = pmbus_read_status_word(client, i);
> if (status < 0) {
> mutex_unlock(&data->update_lock);
> @@ -3120,8 +3156,10 @@ static irqreturn_t pmbus_fault_handler(int irq, void *pdata)
>
> if (status & ~(PB_STATUS_OFF | PB_STATUS_BUSY | PB_STATUS_POWER_GOOD_N))
> pmbus_clear_fault_page(client, i);
> +
> + mutex_unlock(&data->update_lock);
> }
> - mutex_unlock(&data->update_lock);
> +

This would add a second empty line (not that it matters because the code
should not change the locking in the first place).

>
> return IRQ_HANDLED;
> }

2023-02-14 14:11:25

by Naresh Solanki

[permalink] [raw]
Subject: Re: [PATCH v2 2/4] hwmon: (pmbus/core) Generalise pmbus get status

Hi

On 11-02-2023 08:37 pm, Guenter Roeck wrote:
> On Tue, Feb 07, 2023 at 01:02:39PM +0100, Naresh Solanki wrote:
>> Add function pmbus get status that can be used to get both pmbus
>> specific status & regulator status
>>
>> Signed-off-by: Naresh Solanki <[email protected]>
>> ...
>> Changes in V2:
>> - Add __maybe_unused attribute for pmbus_get_status function
>> - Remove unrelated changes
>> ---
>> drivers/hwmon/pmbus/pmbus_core.c | 118 +++++++++++++++++--------------
>> 1 file changed, 66 insertions(+), 52 deletions(-)
>>
>> diff --git a/drivers/hwmon/pmbus/pmbus_core.c b/drivers/hwmon/pmbus/pmbus_core.c
>> index 1b70cf3be313..5ccae8126a56 100644
>> --- a/drivers/hwmon/pmbus/pmbus_core.c
>> +++ b/drivers/hwmon/pmbus/pmbus_core.c
>> @@ -2735,61 +2735,14 @@ static const struct pmbus_status_category __maybe_unused pmbus_status_flag_map[]
>> },
>> };
>>
>> -#if IS_ENABLED(CONFIG_REGULATOR)
>> -static int pmbus_regulator_is_enabled(struct regulator_dev *rdev)
>> -{
>> - 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;
>> -
>> - return !!(ret & PB_OPERATION_CONTROL_ON);
>> -}
>> -
>> -static int _pmbus_regulator_on_off(struct regulator_dev *rdev, bool enable)
>> -{
>> - 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_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)
>> -{
>> - return _pmbus_regulator_on_off(rdev, 1);
>> -}
>> -
>> -static int pmbus_regulator_disable(struct regulator_dev *rdev)
>> -{
>> - return _pmbus_regulator_on_off(rdev, 0);
>> -}
>>
>> -static int pmbus_regulator_get_error_flags(struct regulator_dev *rdev, unsigned int *flags)
>> +static int __maybe_unused pmbus_get_flags(struct pmbus_data *data, u8 page, unsigned int *flags)
>> {
>> - int i, status;
>> + int i, status, ret;
>> const struct pmbus_status_category *cat;
>> const struct pmbus_status_assoc *bit;
>> - 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);
>> + struct device *dev = data->dev;
>> + struct i2c_client *client = to_i2c_client(dev);
>> int func = data->info->func[page];
>>
>> *flags = 0;
>> @@ -2827,7 +2780,13 @@ static int pmbus_regulator_get_error_flags(struct regulator_dev *rdev, unsigned
>> if (status < 0)
>> return status;
>>
>> - if (pmbus_regulator_is_enabled(rdev)) {
>> + mutex_lock(&data->update_lock);
>> + ret = _pmbus_read_byte_data(client, page, PMBUS_OPERATION);
>> + mutex_unlock(&data->update_lock);
>> + if (ret < 0)
>> + return status;
>> +
>> + if (ret & PB_OPERATION_CONTROL_ON) {
>
> This code is now executed twice. Please keep the original function,
> just rename it to pmbus_is_enabled() or similar, then call it from
> pmbus_regulator_is_enabled() which then just needs to adjust the
> parameter (and maybe pass 'dev' as argument).
>
Sure
>> if (status & PB_STATUS_OFF)
>> *flags |= REGULATOR_ERROR_FAIL;
>>
>> @@ -2855,6 +2814,61 @@ static int pmbus_regulator_get_error_flags(struct regulator_dev *rdev, unsigned
>> return 0;
>> }
>>
>> +#if IS_ENABLED(CONFIG_REGULATOR)
>> +static int pmbus_regulator_is_enabled(struct regulator_dev *rdev)
>> +{
>> + 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;
>> +
>> + return !!(ret & PB_OPERATION_CONTROL_ON);
>
> This could then be as simple as
>
> return pmbus_is_enabled(rdev_get_dev(rdev));
>
Sure
> Thanks,
> Guenter
>
>> +}
>> +
>> +static int _pmbus_regulator_on_off(struct regulator_dev *rdev, bool enable)
>> +{
>> + 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_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)
>> +{
>> + return _pmbus_regulator_on_off(rdev, 1);
>> +}
>> +
>> +static int pmbus_regulator_disable(struct regulator_dev *rdev)
>> +{
>> + return _pmbus_regulator_on_off(rdev, 0);
>> +}
>> +
>> +static int pmbus_regulator_get_error_flags(struct regulator_dev *rdev, unsigned int *flags)
>> +{
>> + struct device *dev = rdev_get_dev(rdev);
>> + struct i2c_client *client = to_i2c_client(dev->parent);
>> + struct pmbus_data *data = i2c_get_clientdata(client);
>> +
>> + return pmbus_get_flags(data, rdev_get_id(rdev), flags);
>> +}
>> +
>> static int pmbus_regulator_get_status(struct regulator_dev *rdev)
>> {
>> struct device *dev = rdev_get_dev(rdev);

2023-02-14 14:11:32

by Naresh Solanki

[permalink] [raw]
Subject: Re: [PATCH v2 3/4] hwmon: (pmbus/core): Add interrupt support

Hi

On 11-02-2023 09:07 pm, Guenter Roeck wrote:
> On Tue, Feb 07, 2023 at 01:02:40PM +0100, Naresh Solanki wrote:
>> From: Patrick Rudolph <[email protected]>
>>
>> Implement PMBUS irq handler.
>>
>> Signed-off-by: Patrick Rudolph <[email protected]>
>> Signed-off-by: Naresh Solanki <[email protected]>
>> ---
>> drivers/hwmon/pmbus/pmbus.h | 2 +-
>> drivers/hwmon/pmbus/pmbus_core.c | 85 ++++++++++++++++++++++++++++++++
>> 2 files changed, 86 insertions(+), 1 deletion(-)
>>
>> diff --git a/drivers/hwmon/pmbus/pmbus.h b/drivers/hwmon/pmbus/pmbus.h
>> index 713ea7915425..11e84e141126 100644
>> --- a/drivers/hwmon/pmbus/pmbus.h
>> +++ b/drivers/hwmon/pmbus/pmbus.h
>> @@ -26,7 +26,7 @@ enum pmbus_regs {
>>
>> PMBUS_CAPABILITY = 0x19,
>> PMBUS_QUERY = 0x1A,
>> -
>> + PMBUS_SMBALERT_MASK = 0x1B,
>> PMBUS_VOUT_MODE = 0x20,
>> PMBUS_VOUT_COMMAND = 0x21,
>> PMBUS_VOUT_TRIM = 0x22,
>> diff --git a/drivers/hwmon/pmbus/pmbus_core.c b/drivers/hwmon/pmbus/pmbus_core.c
>> index 5ccae8126a56..d5403baad60a 100644
>> --- a/drivers/hwmon/pmbus/pmbus_core.c
>> +++ b/drivers/hwmon/pmbus/pmbus_core.c
>> @@ -3093,6 +3093,85 @@ static int pmbus_regulator_register(struct pmbus_data *data)
>> }
>> #endif
>>
>> +static int pmbus_write_smbalert_mask(struct i2c_client *client, u8 page, u8 reg, u8 val)
>> +{
>> + int err;
>> +
>> + err = pmbus_check_word_register(client, page, reg | (val << 8));
>> + if (err)
>> + return err;
>
> I am not convinced that this is necessary. The next command will return an
> error anyway if the register or the specific mask is not supported, so what
> is the point ?
>
Sure. will remove.
>> +
>> + return pmbus_write_word_data(client, page, PMBUS_SMBALERT_MASK, reg | (val << 8));
>> +}
>> +
>> +static irqreturn_t pmbus_fault_handler(int irq, void *pdata)
>> +{
>> + struct pmbus_data *data = pdata;
>> + struct i2c_client *client = to_i2c_client(data->dev);
>> + int i, status;
>> +
>> + mutex_lock(&data->update_lock);
>> + for (i = 0; i < data->info->pages; i++) {
>> + status = pmbus_read_status_word(client, i);
>> + if (status < 0) {
>> + mutex_unlock(&data->update_lock);
>> + return status;
>> + }
>> +
>> + if (status & ~(PB_STATUS_OFF | PB_STATUS_BUSY | PB_STATUS_POWER_GOOD_N))
>> + pmbus_clear_fault_page(client, i);
>> + }
>> + mutex_unlock(&data->update_lock);
>> +
>> + return IRQ_HANDLED;
>> +}
>> +
>> +static int pmbus_irq_setup(struct i2c_client *client, struct pmbus_data *data)
>> +{
>> + struct device *dev = &client->dev;
>> + const struct pmbus_status_category *cat;
>> + const struct pmbus_status_assoc *bit;
>> + int i, j, err, ret, func;
>> + u8 mask;
>> + u8 misc_status[] = {PMBUS_STATUS_CML, PMBUS_STATUS_OTHER, PMBUS_STATUS_MFR_SPECIFIC,
>> + PMBUS_STATUS_FAN_12, PMBUS_STATUS_FAN_34};
>
> static const u8 ...
>
Done
>> +
>> + for (i = 0; i < data->info->pages; i++) {
>> + func = data->info->func[i];
>> +
>> + for (j = 0; j < ARRAY_SIZE(pmbus_status_flag_map); j++) {
>> + cat = &pmbus_status_flag_map[j];
>> + if (!(func & cat->func))
>> + continue;
>> + mask = 0;
>> + for (bit = cat->bits; bit->pflag; bit++)
>> + mask |= bit->pflag;
>> +
>> + err = pmbus_write_smbalert_mask(client, i, cat->reg, ~mask);
>> + if (err)
>> + dev_err_once(dev, "Failed to set smbalert for reg 0x%02x\n",
>> + cat->reg);
>
> dev_err implies an error. This is ignored and thus not an error. On top of that,
> not all chips support PMBUS_SMBALERT_MASK. All of those would see this message.
> We can't have that. At best make it a dev_dbg.
>
Sure. Will make it dev_dbg_once.
>> + }
>> +
>> + for (j = 0; j < ARRAY_SIZE(misc_status); j++) {
>> + err = pmbus_write_smbalert_mask(client, i, misc_status[j], 0xff);
>> + if (err)
>> + dev_err_once(dev, "Failed to set smbalert for reg 0x%02x\n",
>> + misc_status[j]);
>
> We definitely can't have a message here; that would fire for almost
> every chip.
>
Sure. Will remove printing of error here.
>> + }
>> + }
>> +
>> + /* Register notifiers - can fail if IRQ is not given */
>
> If there is no irq, what is the point of executing this code in the first
> place ? No, wait, in that case the function isn't called in the first place.
> I think the comment doesn't add any value and is just confusing.
>
Will clean this comment.
>> + ret = devm_request_threaded_irq(dev, client->irq, NULL, pmbus_fault_handler, 0,
>> + "pmbus-irq", data);
>> + if (ret) {
>
> Why both "err" and "ret" ?
>
Will replace ret with err.
>> + dev_warn(dev, "IRQ disabled %d\n", ret);
>
> The calling code aborts, so this should be dev_err() and say something
> better than "IRQ disabled"; It should be something like "failed to
> request irq".
>
Sure. Will update to "failed to request an irq"
>> + return ret;
>> + }
>> +
>> + return 0;
>> +}
>> +
>> static struct dentry *pmbus_debugfs_dir; /* pmbus debugfs directory */
>>
>> #if IS_ENABLED(CONFIG_DEBUG_FS)
>> @@ -3455,6 +3534,12 @@ int pmbus_do_probe(struct i2c_client *client, struct pmbus_driver_info *info)
>> if (ret)
>> return ret;
>>
>> + if (client->irq > 0) {
>> + ret = pmbus_irq_setup(client, data);
>> + if (ret)
>> + return ret;
>> + }
>> +
>
> I think it would be better to have the check in pmbus_irq_setup():
>
> pmbus_irq_setup()
> {
> if (!client->irq)
> return 0;
>
> ...
> }
>
> and here
> ret = pmbus_irq_setup(client, data);
> if (ret)
> return ret;
>
>
Sure
>> ret = pmbus_init_debugfs(client, data);
>> if (ret)
>> dev_warn(dev, "Failed to register debugfs\n");

2023-02-14 14:13:22

by Naresh Solanki

[permalink] [raw]
Subject: Re: [PATCH v2 4/4] hwmon: (pmbus/core): Notify hwmon events

Hi,

On 11-02-2023 09:16 pm, Guenter Roeck wrote:
> On Tue, Feb 07, 2023 at 01:02:41PM +0100, Naresh Solanki wrote:
>> From: Patrick Rudolph <[email protected]>
>>
>> Notify hwmon events using the pmbus irq handler.
>>
>> Signed-off-by: Patrick Rudolph <[email protected]>
>> Signed-off-by: Naresh Solanki <[email protected]>
>> ...
>> Changes in V2
>> - Remove __maybe_unsed attribute as its not needed.
>> ---
>> drivers/hwmon/pmbus/pmbus_core.c | 48 ++++++++++++++++++++++++++++----
>> 1 file changed, 43 insertions(+), 5 deletions(-)
>>
>> diff --git a/drivers/hwmon/pmbus/pmbus_core.c b/drivers/hwmon/pmbus/pmbus_core.c
>> index d5403baad60a..f6778a9c7126 100644
>> --- a/drivers/hwmon/pmbus/pmbus_core.c
>> +++ b/drivers/hwmon/pmbus/pmbus_core.c
>> @@ -2735,8 +2735,36 @@ static const struct pmbus_status_category __maybe_unused pmbus_status_flag_map[]
>> },
>> };
>>
>> +#define to_dev_attr(_dev_attr) \
>> + container_of(_dev_attr, struct device_attribute, attr)
>> +
>> +static void pmbus_notify(struct pmbus_data *data, int page, int reg, int flags)
>> +{
>> + int i;
>> +
>> + for (i = 0; i < data->num_attributes; i++) {
>> + struct device_attribute *da = to_dev_attr(data->group.attrs[i]);
>> + struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
>> + int index = attr->index;
>> + u16 smask = pb_index_to_mask(index);
>> + u8 spage = pb_index_to_page(index);
>> + u16 sreg = pb_index_to_reg(index);
>> +
>> + if (reg == sreg && page == spage && (smask & flags)) {
>> + dev_dbg(data->dev, "sysfs notify: %s", da->attr.name);
>> + sysfs_notify(&data->dev->kobj, NULL, da->attr.name);
>> + kobject_uevent(&data->dev->kobj, KOBJ_CHANGE);
>> + flags &= ~smask;
>> + }
>> +
>> + if (!flags)
>> + break;
>> + }
>> +}
>> +
>> +static int pmbus_get_flags(struct pmbus_data *data, u8 page, unsigned int *flags,
>> + bool notify)
>>
>> -static int __maybe_unused pmbus_get_flags(struct pmbus_data *data, u8 page, unsigned int *flags)
>> {
>> int i, status, ret;
>> const struct pmbus_status_category *cat;
>> @@ -2764,6 +2792,10 @@ static int __maybe_unused pmbus_get_flags(struct pmbus_data *data, u8 page, unsi
>> if (status & bit->pflag)
>> *flags |= bit->rflag;
>> }
>> +
>> + if (notify && status)
>> + pmbus_notify(data, page, cat->reg, status);
>> +
>> }
>>
>> /*
>> @@ -2866,7 +2898,7 @@ static int pmbus_regulator_get_error_flags(struct regulator_dev *rdev, unsigned
>> struct i2c_client *client = to_i2c_client(dev->parent);
>> struct pmbus_data *data = i2c_get_clientdata(client);
>>
>> - return pmbus_get_flags(data, rdev_get_id(rdev), flags);
>> + return pmbus_get_flags(data, rdev_get_id(rdev), flags, false);
>> }
>>
>> static int pmbus_regulator_get_status(struct regulator_dev *rdev)
>> @@ -3108,10 +3140,14 @@ static irqreturn_t pmbus_fault_handler(int irq, void *pdata)
>> {
>> struct pmbus_data *data = pdata;
>> struct i2c_client *client = to_i2c_client(data->dev);
>> - int i, status;
>> + int i, status, ret;
>>
>> - mutex_lock(&data->update_lock);
>> for (i = 0; i < data->info->pages; i++) {
>> + ret = pmbus_get_flags(data, i, &status, true);
>> + if (ret)
>> + return ret;
>> +
>> + mutex_lock(&data->update_lock);
>
> You should introduce a locked version of pmbus_get_flags() and call
> that function, and keep the existing locking in place.
>
I'm not sure if you meant to have pmbus_get_flags that wont use lock?

>> status = pmbus_read_status_word(client, i);
>> if (status < 0) {
>> mutex_unlock(&data->update_lock);
>> @@ -3120,8 +3156,10 @@ static irqreturn_t pmbus_fault_handler(int irq, void *pdata)
>>
>> if (status & ~(PB_STATUS_OFF | PB_STATUS_BUSY | PB_STATUS_POWER_GOOD_N))
>> pmbus_clear_fault_page(client, i);
>> +
>> + mutex_unlock(&data->update_lock);
>> }
>> - mutex_unlock(&data->update_lock);
>> +
>
> This would add a second empty line (not that it matters because the code
> should not change the locking in the first place).
>
Will remove the new line
>>
>> return IRQ_HANDLED;
>> }

2023-02-14 14:55:06

by Guenter Roeck

[permalink] [raw]
Subject: Re: [PATCH v2 4/4] hwmon: (pmbus/core): Notify hwmon events

On 2/14/23 06:11, Naresh Solanki wrote:
> Hi,
>
> On 11-02-2023 09:16 pm, Guenter Roeck wrote:
>> On Tue, Feb 07, 2023 at 01:02:41PM +0100, Naresh Solanki wrote:
>>> From: Patrick Rudolph <[email protected]>
>>>
>>> Notify hwmon events using the pmbus irq handler.
>>>
>>> Signed-off-by: Patrick Rudolph <[email protected]>
>>> Signed-off-by: Naresh Solanki <[email protected]>
>>> ...
>>> Changes in V2
>>> - Remove __maybe_unsed attribute as its not needed.
>>> ---
>>>   drivers/hwmon/pmbus/pmbus_core.c | 48 ++++++++++++++++++++++++++++----
>>>   1 file changed, 43 insertions(+), 5 deletions(-)
>>>
>>> diff --git a/drivers/hwmon/pmbus/pmbus_core.c b/drivers/hwmon/pmbus/pmbus_core.c
>>> index d5403baad60a..f6778a9c7126 100644
>>> --- a/drivers/hwmon/pmbus/pmbus_core.c
>>> +++ b/drivers/hwmon/pmbus/pmbus_core.c
>>> @@ -2735,8 +2735,36 @@ static const struct pmbus_status_category __maybe_unused pmbus_status_flag_map[]
>>>       },
>>>   };
>>> +#define to_dev_attr(_dev_attr) \
>>> +    container_of(_dev_attr, struct device_attribute, attr)
>>> +
>>> +static void pmbus_notify(struct pmbus_data *data, int page, int reg, int flags)
>>> +{
>>> +    int i;
>>> +
>>> +    for (i = 0; i < data->num_attributes; i++) {
>>> +        struct device_attribute *da = to_dev_attr(data->group.attrs[i]);
>>> +        struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
>>> +        int index = attr->index;
>>> +        u16 smask = pb_index_to_mask(index);
>>> +        u8 spage = pb_index_to_page(index);
>>> +        u16 sreg = pb_index_to_reg(index);
>>> +
>>> +        if (reg == sreg && page == spage && (smask & flags)) {
>>> +            dev_dbg(data->dev, "sysfs notify: %s", da->attr.name);
>>> +            sysfs_notify(&data->dev->kobj, NULL, da->attr.name);
>>> +            kobject_uevent(&data->dev->kobj, KOBJ_CHANGE);
>>> +            flags &= ~smask;
>>> +        }
>>> +
>>> +        if (!flags)
>>> +            break;
>>> +    }
>>> +}
>>> +
>>> +static int pmbus_get_flags(struct pmbus_data *data, u8 page, unsigned int *flags,
>>> +               bool notify)
>>> -static int __maybe_unused pmbus_get_flags(struct pmbus_data *data, u8 page, unsigned int *flags)
>>>   {
>>>       int i, status, ret;
>>>       const struct pmbus_status_category *cat;
>>> @@ -2764,6 +2792,10 @@ static int __maybe_unused pmbus_get_flags(struct pmbus_data *data, u8 page, unsi
>>>               if (status & bit->pflag)
>>>                   *flags |= bit->rflag;
>>>           }
>>> +
>>> +        if (notify && status)
>>> +            pmbus_notify(data, page, cat->reg, status);
>>> +
>>>       }
>>>       /*
>>> @@ -2866,7 +2898,7 @@ static int pmbus_regulator_get_error_flags(struct regulator_dev *rdev, unsigned
>>>       struct i2c_client *client = to_i2c_client(dev->parent);
>>>       struct pmbus_data *data = i2c_get_clientdata(client);
>>> -    return pmbus_get_flags(data, rdev_get_id(rdev), flags);
>>> +    return pmbus_get_flags(data, rdev_get_id(rdev), flags, false);
>>>   }
>>>   static int pmbus_regulator_get_status(struct regulator_dev *rdev)
>>> @@ -3108,10 +3140,14 @@ static irqreturn_t pmbus_fault_handler(int irq, void *pdata)
>>>   {
>>>       struct pmbus_data *data = pdata;
>>>       struct i2c_client *client = to_i2c_client(data->dev);
>>> -    int i, status;
>>> +    int i, status, ret;
>>> -    mutex_lock(&data->update_lock);
>>>       for (i = 0; i < data->info->pages; i++) {
>>> +        ret = pmbus_get_flags(data, i, &status, true);
>>> +        if (ret)
>>> +            return ret;
>>> +
>>> +        mutex_lock(&data->update_lock);
>>
>> You should introduce a locked version of pmbus_get_flags() and call
>> that function, and keep the existing locking in place.
>>
> I'm not sure if you meant to have pmbus_get_flags that wont use lock?
>


__pmbus_get_flags(...)
{
/* no lock acquired here */
}

pmbus_get_flags(...)
{
int ret;

mutex_lock(&data->update_lock);
ret = __pmbus_get_flags(...);
mutex_unlock(&data->update_lock);
return ret;
}

Then call __pmbus_get_flags() from above code.

Guenter

>>>           status = pmbus_read_status_word(client, i);
>>>           if (status < 0) {
>>>               mutex_unlock(&data->update_lock);
>>> @@ -3120,8 +3156,10 @@ static irqreturn_t pmbus_fault_handler(int irq, void *pdata)
>>>           if (status & ~(PB_STATUS_OFF | PB_STATUS_BUSY | PB_STATUS_POWER_GOOD_N))
>>>               pmbus_clear_fault_page(client, i);
>>> +
>>> +        mutex_unlock(&data->update_lock);
>>>       }
>>> -    mutex_unlock(&data->update_lock);
>>> +
>>
>> This would add a second empty line (not that it matters because the code
>> should not change the locking in the first place).
>>
> Will remove the new line
>>>       return IRQ_HANDLED;
>>>   }


2023-02-14 15:23:03

by Naresh Solanki

[permalink] [raw]
Subject: Re: [PATCH v2 4/4] hwmon: (pmbus/core): Notify hwmon events

Hi

On 14-02-2023 08:24 pm, Guenter Roeck wrote:
> On 2/14/23 06:11, Naresh Solanki wrote:
>> Hi,
>>
>> On 11-02-2023 09:16 pm, Guenter Roeck wrote:
>>> On Tue, Feb 07, 2023 at 01:02:41PM +0100, Naresh Solanki wrote:
>>>> From: Patrick Rudolph <[email protected]>
>>>>
>>>> Notify hwmon events using the pmbus irq handler.
>>>>
>>>> Signed-off-by: Patrick Rudolph <[email protected]>
>>>> Signed-off-by: Naresh Solanki <[email protected]>
>>>> ...
>>>> Changes in V2
>>>> - Remove __maybe_unsed attribute as its not needed.
>>>> ---
>>>>   drivers/hwmon/pmbus/pmbus_core.c | 48
>>>> ++++++++++++++++++++++++++++----
>>>>   1 file changed, 43 insertions(+), 5 deletions(-)
>>>>
>>>> diff --git a/drivers/hwmon/pmbus/pmbus_core.c
>>>> b/drivers/hwmon/pmbus/pmbus_core.c
>>>> index d5403baad60a..f6778a9c7126 100644
>>>> --- a/drivers/hwmon/pmbus/pmbus_core.c
>>>> +++ b/drivers/hwmon/pmbus/pmbus_core.c
>>>> @@ -2735,8 +2735,36 @@ static const struct pmbus_status_category
>>>> __maybe_unused pmbus_status_flag_map[]
>>>>       },
>>>>   };
>>>> +#define to_dev_attr(_dev_attr) \
>>>> +    container_of(_dev_attr, struct device_attribute, attr)
>>>> +
>>>> +static void pmbus_notify(struct pmbus_data *data, int page, int
>>>> reg, int flags)
>>>> +{
>>>> +    int i;
>>>> +
>>>> +    for (i = 0; i < data->num_attributes; i++) {
>>>> +        struct device_attribute *da =
>>>> to_dev_attr(data->group.attrs[i]);
>>>> +        struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
>>>> +        int index = attr->index;
>>>> +        u16 smask = pb_index_to_mask(index);
>>>> +        u8 spage = pb_index_to_page(index);
>>>> +        u16 sreg = pb_index_to_reg(index);
>>>> +
>>>> +        if (reg == sreg && page == spage && (smask & flags)) {
>>>> +            dev_dbg(data->dev, "sysfs notify: %s", da->attr.name);
>>>> +            sysfs_notify(&data->dev->kobj, NULL, da->attr.name);
>>>> +            kobject_uevent(&data->dev->kobj, KOBJ_CHANGE);
>>>> +            flags &= ~smask;
>>>> +        }
>>>> +
>>>> +        if (!flags)
>>>> +            break;
>>>> +    }
>>>> +}
>>>> +
>>>> +static int pmbus_get_flags(struct pmbus_data *data, u8 page,
>>>> unsigned int *flags,
>>>> +               bool notify)
>>>> -static int __maybe_unused pmbus_get_flags(struct pmbus_data *data,
>>>> u8 page, unsigned int *flags)
>>>>   {
>>>>       int i, status, ret;
>>>>       const struct pmbus_status_category *cat;
>>>> @@ -2764,6 +2792,10 @@ static int __maybe_unused
>>>> pmbus_get_flags(struct pmbus_data *data, u8 page, unsi
>>>>               if (status & bit->pflag)
>>>>                   *flags |= bit->rflag;
>>>>           }
>>>> +
>>>> +        if (notify && status)
>>>> +            pmbus_notify(data, page, cat->reg, status);
>>>> +
>>>>       }
>>>>       /*
>>>> @@ -2866,7 +2898,7 @@ static int
>>>> pmbus_regulator_get_error_flags(struct regulator_dev *rdev, unsigned
>>>>       struct i2c_client *client = to_i2c_client(dev->parent);
>>>>       struct pmbus_data *data = i2c_get_clientdata(client);
>>>> -    return pmbus_get_flags(data, rdev_get_id(rdev), flags);
>>>> +    return pmbus_get_flags(data, rdev_get_id(rdev), flags, false);
>>>>   }
>>>>   static int pmbus_regulator_get_status(struct regulator_dev *rdev)
>>>> @@ -3108,10 +3140,14 @@ static irqreturn_t pmbus_fault_handler(int
>>>> irq, void *pdata)
>>>>   {
>>>>       struct pmbus_data *data = pdata;
>>>>       struct i2c_client *client = to_i2c_client(data->dev);
>>>> -    int i, status;
>>>> +    int i, status, ret;
>>>> -    mutex_lock(&data->update_lock);
>>>>       for (i = 0; i < data->info->pages; i++) {
>>>> +        ret = pmbus_get_flags(data, i, &status, true);
>>>> +        if (ret)
>>>> +            return ret;
>>>> +
>>>> +        mutex_lock(&data->update_lock);
>>>
>>> You should introduce a locked version of pmbus_get_flags() and call
>>> that function, and keep the existing locking in place.
>>>
>> I'm not sure if you meant to have pmbus_get_flags that wont use lock?
>>
>
>
> __pmbus_get_flags(...)
> {
>     /* no lock acquired here */
> }
>
> pmbus_get_flags(...)
> {
>     int ret;
>
>     mutex_lock(&data->update_lock);
>     ret = __pmbus_get_flags(...);
>     mutex_unlock(&data->update_lock);
>     return ret;
> }
>
> Then call __pmbus_get_flags() from above code.
Sure. Will add that change.
>
> Guenter
>
>>>>           status = pmbus_read_status_word(client, i);
>>>>           if (status < 0) {
>>>>               mutex_unlock(&data->update_lock);
>>>> @@ -3120,8 +3156,10 @@ static irqreturn_t pmbus_fault_handler(int
>>>> irq, void *pdata)
>>>>           if (status & ~(PB_STATUS_OFF | PB_STATUS_BUSY |
>>>> PB_STATUS_POWER_GOOD_N))
>>>>               pmbus_clear_fault_page(client, i);
>>>> +
>>>> +        mutex_unlock(&data->update_lock);
>>>>       }
>>>> -    mutex_unlock(&data->update_lock);
>>>> +
>>>
>>> This would add a second empty line (not that it matters because the code
>>> should not change the locking in the first place).
>>>
>> Will remove the new line
>>>>       return IRQ_HANDLED;
>>>>   }
>