Skip to content
30 changes: 29 additions & 1 deletion Documentation/devicetree/bindings/hwmon/pmbus/adi,max20830.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,13 @@ allOf:

properties:
compatible:
const: adi,max20830
oneOf:
- const: adi,max20830
- items:
- enum:
- adi,max20830c
- adi,max20840c
- const: adi,max20830 # Fallback compatible

reg:
maxItems: 1
Expand All @@ -39,16 +45,36 @@ properties:
description:
Optional 2.5V to 5.5V LDO input supply.

enable-gpios:
description:
GPIO connected to the EN (enable) pin.
maxItems: 1

pwr-good-gpios:
description:
GPIO connected to the power-good status output pin.
maxItems: 1

adi,vout-rfb1-ohms:
description:
Top feedback resistor (RFB1) value in ohms for VOUT sensing divider.
When the desired output voltage is higher than VREF, a resistor divider
is required. VOUT = VREF × (1 + RFB1/RFB2)

adi,vout-rfb2-ohms:
description:
Bottom feedback resistor (RFB2) value in ohms for VOUT sensing divider.
Datasheet recommends that RFB2 does not exceed 2.5kΩ.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should er have some constrains for the above values?!

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the datasheet doesn't specificy any hard max/min limits for the resistor divider. So I assumed this to be ok.
but i did miss a zero check on one of them that will cause some division error


required:
- compatible
- reg
- vddh-supply

dependencies:
adi,vout-rfb1-ohms: ['adi,vout-rfb2-ohms']
adi,vout-rfb2-ohms: ['adi,vout-rfb1-ohms']

unevaluatedProperties: false

examples:
Expand All @@ -61,6 +87,8 @@ examples:
compatible = "adi,max20830";
reg = <0x30>;
vddh-supply = <&vddh>;
adi,vout-rfb1-ohms = <10000>;
adi,vout-rfb2-ohms = <2000>;
};
};
...
27 changes: 22 additions & 5 deletions Documentation/hwmon/max20830.rst
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,22 @@ Supported chips:

Datasheet: https://www.analog.com/media/en/technical-documentation/data-sheets/max20830.pdf

* Analog Devices MAX20830C

Prefix: 'max20830c'

Addresses scanned: -

Datasheet:

* Analog Devices MAX20840C

Prefix: 'max20840c'

Addresses scanned: -

Datasheet:

Author:

- Alexis Czezar Torreno <alexisczezar.torreno@analog.com>
Expand All @@ -21,12 +37,13 @@ Author:
Description
-----------

This driver supports hardware monitoring for Analog Devices MAX20830
Step-Down Switching Regulator with PMBus Interface.
This driver supports hardware monitoring for Analog Devices MAX20830, MAX20830C
and MAX20840C. These are Step-Down Switching Regulator with PMBus Interface.

The MAX20830 is a 2.7V to 16V, 30A fully integrated step-down DC-DC switching
regulator. Through the PMBus interface, the device can monitor input/output
voltages, output current and temperature.
MAX20830, and MAX20830C are 2.7V to 16V, 30A fully integrated step-down DC-DC
switching regulators. MAX20840C is similar but can reach 40A. Through the PMBus
interface, these devices can monitor input/output voltages, output current and
temperature.

The driver is a client driver to the core PMBus driver. Please see
Documentation/hwmon/pmbus.rst for details on PMBus client drivers.
Expand Down
102 changes: 88 additions & 14 deletions drivers/hwmon/pmbus/max20830.c
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Hardware monitoring driver for Analog Devices MAX20830
Expand All @@ -7,12 +7,70 @@

#include <linux/errno.h>
#include <linux/i2c.h>
#include <linux/math64.h>
#include <linux/mod_devicetable.h>
#include <linux/module.h>
#include <linux/string.h>
#include "pmbus.h"

#define MAX20830_IC_DEVICE_ID_LENGTH 9

struct max20830_data {
struct pmbus_driver_info info;
u32 vout_rfb1;
u32 vout_rfb2;
};

static const char * const supported_chip_ids[] = {
"MAX20830",
"MAX20830C",
"MAX20840C",
};

/*
* MAX20830 only supports READ_VOUT for VOUT monitoring.
*
* Limit registers (VOUT_OV_WARN_LIMIT, VOUT_OV_FAULT_LIMIT, etc.) are not
* supported by this driver and return -ENODATA. This means sysfs attributes
* like in1_max, in1_crit, etc. will not be available. Only in1_input (the
* scaled output voltage) is supported.
*
* MAX20830 uses an external resistor divider for voltage sensing:
* - VOUT_COMMAND and VOUT_MAX set the reference voltage at the feedback pin
* - READ_VOUT reports the feedback voltage, which needs to be scaled for actual
* output voltage
*
* Scaling formula: vout_actual = vout_fb × (1 + RFB1 / RFB2)
*
* If regulator support is added in the future, some adjustments are needed to
* ensure correct feedback voltages are set.
*/
static int max20830_read_word_data(struct i2c_client *client, int page,
int phase, int reg)
{
const struct pmbus_driver_info *info = pmbus_get_driver_info(client);
const struct max20830_data *data = container_of(info, struct max20830_data, info);
int ret;
u64 temp;

switch (reg) {
case PMBUS_READ_VOUT:
ret = pmbus_read_word_data(client, page, phase, reg);
if (ret < 0)
return ret;

/* Apply voltage divider scaling if resistors are non-zero */
if (data->vout_rfb1 && data->vout_rfb2) {
temp = (u64)data->vout_rfb1 + (u64)data->vout_rfb2;
temp = DIV_ROUND_CLOSEST_ULL((u64)ret * temp, data->vout_rfb2);
ret = clamp_val(temp, 0, 0xFFFF);
}
return ret;
default:
return -ENODATA;
}
}

static struct pmbus_driver_info max20830_info = {
.pages = 1,
.format[PSC_VOLTAGE_IN] = linear,
Expand All @@ -23,12 +81,24 @@
PMBUS_HAVE_TEMP |
PMBUS_HAVE_STATUS_VOUT | PMBUS_HAVE_STATUS_IOUT |
PMBUS_HAVE_STATUS_INPUT | PMBUS_HAVE_STATUS_TEMP,
.read_word_data = max20830_read_word_data,
};

static int max20830_probe(struct i2c_client *client)
{
u8 buf[I2C_SMBUS_BLOCK_MAX + 1] = {};
int ret;
struct max20830_data *data;
int i, ret;

data = devm_kzalloc(&client->dev, sizeof(*data), GFP_KERNEL);
if (!data)
return -ENOMEM;

data->info = max20830_info;

/* Read optional voltage divider resistor values */
device_property_read_u32(&client->dev, "adi,vout-rfb1-ohms", &data->vout_rfb1);
device_property_read_u32(&client->dev, "adi,vout-rfb2-ohms", &data->vout_rfb2);

if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_READ_BLOCK_DATA) &&
!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_READ_I2C_BLOCK))
Expand All @@ -40,13 +110,12 @@
* which do not support SMBus block reads.
*/
if (i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_READ_BLOCK_DATA)) {
/* Reads 9 Data bytes from MAX20830 */
ret = i2c_smbus_read_block_data(client, PMBUS_IC_DEVICE_ID, buf);
if (ret < 0)
return dev_err_probe(&client->dev, ret,
"Failed to read IC_DEVICE_ID\n");
} else {
/* Reads 1 length byte + 9 Data bytes from MAX20830 */
/* Reads 1 length byte + data bytes */
ret = i2c_smbus_read_i2c_block_data(client, PMBUS_IC_DEVICE_ID,
MAX20830_IC_DEVICE_ID_LENGTH + 1,
buf);
Expand All @@ -62,22 +131,27 @@
ret = ret - 1;
}

/*
* MAX20830 IC_DEVICE_ID sends string data "MAX20830\0".
* Return value should at least be 9 bytes of data.
*/
/* Verify we read the expected number of bytes */
if (ret < MAX20830_IC_DEVICE_ID_LENGTH)
return dev_err_probe(&client->dev, -ENODEV,
"IC_DEVICE_ID too short: expected at least 9 bytes, got %d\n",
ret);
"IC_DEVICE_ID too short: expected %d bytes, got %d\n",
MAX20830_IC_DEVICE_ID_LENGTH, ret);

/* Null-terminate the string */
buf[ret] = '\0';

/* Verify the device ID matches what we expect */
for (i = 0; i < ARRAY_SIZE(supported_chip_ids); i++) {
if (!strcmp(buf, supported_chip_ids[i]))
break;
}

/* 9 bytes of data, buf[0]-buf[7] = "MAX20830", buf[8] = '\0' */
buf[MAX20830_IC_DEVICE_ID_LENGTH - 1] = '\0';
if (strncmp(buf, "MAX20830", MAX20830_IC_DEVICE_ID_LENGTH - 1))
/* No match found - unsupported device */
if (i == ARRAY_SIZE(supported_chip_ids))
return dev_err_probe(&client->dev, -ENODEV,
"Unsupported device: '%s'\n", buf);
"Unsupported device: '%*pE'\n", ret, buf);

return pmbus_do_probe(client, &max20830_info);
return pmbus_do_probe(client, &data->info);
}

static const struct i2c_device_id max20830_id[] = {
Expand Down
Loading