Re: [PATCH 5/5] hwmon: (pmbus) add support for MAX20826 and similar devices
Guenter Roeck <[email protected]> Tue, 28 Jul 2026 15:12:59 -0700
| Newsgroups | org.kernel.vger.linux-hwmon,org.kernel.vger.linux-devicetree,org.kernel.vger.linux-doc |
|---|---|
| Message-ID | <[email protected]> |
On 7/28/26 09:03, Nuno Sá via B4 Relay wrote: > From: Nuno Sá <[email protected]> > > The MAX20826 IC and similar provide a high-density, flexible and scalable > dual-loop solution for high current cores for AI applications. These are > dual loop solutions multiphase voltage regulators. Between Rails A and B, > MAX20855B and MAX20908 supports up to 8 phases total configurable from > 8+0 to 4+4 phases, MAX20912 supports up to 12 phases from 12+0 to 6+6, > and MAX20826 and MAX20916 supports up to 16 phases from 16+0 to 8+8. > > Co-developed-by: Alexis Czezar Torreno <[email protected]> > Signed-off-by: Alexis Czezar Torreno <[email protected]> > Signed-off-by: Nuno Sá <[email protected]> Very incomplete review; see it as first glance feedback. Guenter > --- > Documentation/hwmon/index.rst | 1 + > Documentation/hwmon/max20826.rst | 124 +++++ > MAINTAINERS | 2 + > drivers/hwmon/pmbus/Kconfig | 19 + > drivers/hwmon/pmbus/Makefile | 1 + > drivers/hwmon/pmbus/max20826.c | 1037 ++++++++++++++++++++++++++++++++++++++ > 6 files changed, 1184 insertions(+) > > diff --git a/Documentation/hwmon/index.rst b/Documentation/hwmon/index.rst > index 29130df44d12..c63992b292c1 100644 > --- a/Documentation/hwmon/index.rst > +++ b/Documentation/hwmon/index.rst > @@ -164,6 +164,7 @@ Hardware Monitoring Kernel Drivers > max197 > max20730 > max20751 > + max20826 > max20830 > max20860a > max31722 > diff --git a/Documentation/hwmon/max20826.rst b/Documentation/hwmon/max20826.rst > new file mode 100644 > index 000000000000..be97312dc612 > --- /dev/null > +++ b/Documentation/hwmon/max20826.rst > @@ -0,0 +1,124 @@ > +.. SPDX-License-Identifier: GPL-2.0 > + > +Kernel driver max20826 > +====================== > + > +Supported chips: > + > + * Analog Devices MAX20826 > + > + Prefix: 'max20826' > + > + Addresses scanned: - > + > + Datasheet: Datasheet is not publicly available. > + > + * Analog Devices MAX20855B > + > + Prefix: 'max20855b' > + > + Addresses scanned: - > + > + Datasheet: Datasheet is not publicly available. > + > + * Analog Devices MAX20908 > + > + Prefix: 'max20908' > + > + Addresses scanned: - > + > + Datasheet: Datasheet is not publicly available. > + > + * Analog Devices MAX20912 > + > + Prefix: 'max20912' > + > + Addresses scanned: - > + > + Datasheet: Datasheet is not publicly available. > + > + * Analog Devices MAX20916 > + > + Prefix: 'max20916' > + > + Addresses scanned: - > + > + Datasheet: Datasheet is not publicly available. > + > +Author: > + > + - Nuno Sá <[email protected]> > + > + > +Description > +----------- > + > +This driver supports hardware monitoring for Analog Devices MAX20826, > +MAX20855B, MAX20908, MAX20912, and MAX20916 multiphase voltage regulator > +controllers with PMBus interface. > + > +The devices are dual-loop, multiphase controllers. Depending on the device and > +configuration, the high-speed processor voltage-control interface can be > +Nvidia PWMVID, Intel SVID, AMD SVI3, or AVSBus. PMBus is used for monitoring, > +configuration, status, and fault reporting. > + > +The driver detects whether the device uses PMBus page mode or direct address > +mode. In direct address mode, rail B is accessed at the rail A I2C address plus > +one. If rail B is present, the driver exposes a second PMBus page. > + > +The driver detects the active number of phases and exposes per-phase input and > +output current attributes through the PMBus virtual phase support. > + > +Usage Notes > +----------- > + > +This driver does not auto-detect devices. You will have to instantiate the > +devices explicitly. Please see Documentation/i2c/instantiating-devices.rst for > +details. > + > +The optional ``avren`` and ``bvren`` GPIOs may be provided to control the rail A > +and rail B hardware enable pins. If regulator support is enabled, the rails are > +also registered through the PMBus regulator framework. > + > +Sysfs entries > +------------- > + > +The following attributes are supported. Limits, alarms, and per-phase entries > +are exposed depending on device capabilities, rail configuration, and detected > +phase count. > + > +=========================== ================================================ > +in1_label "vin" > +in1_input Measured input voltage > +in1_alarm Input voltage alarm > +in[2-3]_label "vout[1-2]" > +in[2-3]_input Measured output voltage > +in[2-3]_alarm Output voltage alarm > +in[2-3]_high_speed_en Enable high-speed voltage-control interface > +in_high_speed_bus Active high-speed voltage-control interface > +currX_label "iinN", "iinN.P", "ioutN", or "ioutN.P" > +currX_input Measured input/output current > +currX_alarm Current alarm > +powerX_label "pinN" or "poutN" > +powerX_input Measured input/output power > +temp[1-2]_input Measured temperature > +temp[1-2]_alarm Temperature alarm > +=========================== ================================================ > + > +Notes > +----- > + > +``N`` is the rail number, starting at 1. ``P`` is the phase number, starting at > +0. The exact ``currX`` indices depend on the number of present rails and on the > +detected phase count. > + > +``in[2-3]_high_speed_en`` is a per-rail read/write attribute matching the > +standard hwmon voltage channels for the output rails. ``in2_high_speed_en`` > +controls rail A / ``vout1``. ``in3_high_speed_en`` controls rail B / ``vout2`` > +and is only present if rail B is detected. Writing 1 selects the high-speed > +voltage-control interface for that rail; writing 0 selects PMBus voltage > +control. > + > +``in_high_speed_bus`` is a read-only attribute reporting the high-speed > +interface used by the device, for example ``Nvidia PWMVID``, ``Intel SVID``, > +``AMD SVI3``, or ``AVSBus``. > diff --git a/MAINTAINERS b/MAINTAINERS > index fe8808f3ee4c..8518d55800e9 100644 > --- a/MAINTAINERS > +++ b/MAINTAINERS > @@ -15843,6 +15843,8 @@ L: [email protected] > S: Supported > W: https://ez.analog.com/linux-software-drivers > F: Documentation/devicetree/bindings/hwmon/pmbus/adi,max20826.yaml > +F: Documentation/hwmon/max20826.rst > +F: drivers/hwmon/pmbus/max20826.c > > MAX20830 HARDWARE MONITOR DRIVER > M: Alexis Czezar Torreno <[email protected]> > diff --git a/drivers/hwmon/pmbus/Kconfig b/drivers/hwmon/pmbus/Kconfig > index c8cda160b5f8..8d3568f7347d 100644 > --- a/drivers/hwmon/pmbus/Kconfig > +++ b/drivers/hwmon/pmbus/Kconfig > @@ -393,6 +393,25 @@ config SENSORS_MAX20751 > This driver can also be built as a module. If so, the module will > be called max20751. > > +config SENSORS_MAX20826 > + tristate "Analog Devices MAX20826 and similar devices" > + help > + If you say yes here you get hardware monitoring support for Analog > + Devices MAX20826 and similar devices. > + > + This driver can also be built as a module. If so, the module will > + be called max20826. > + > +config SENSORS_MAX20826_REGULATOR > + bool "Regulator support for Analog Devices MAX20826 and similar devices" > + depends on SENSORS_MAX20826 && REGULATOR > + help > + If you say yes here you get regulator support for Analog Devices > + MAX20826 and similar sensors. > + > + This enables the MAX20826 to be used as a regulator device, > + providing voltage control through the regulator framework. > + > config SENSORS_MAX20830 > tristate "Analog Devices MAX20830" > help > diff --git a/drivers/hwmon/pmbus/Makefile b/drivers/hwmon/pmbus/Makefile > index ffc05f493213..15601443470b 100644 > --- a/drivers/hwmon/pmbus/Makefile > +++ b/drivers/hwmon/pmbus/Makefile > @@ -38,6 +38,7 @@ obj-$(CONFIG_SENSORS_MAX16601) += max16601.o > obj-$(CONFIG_SENSORS_MAX17616) += max17616.o > obj-$(CONFIG_SENSORS_MAX20730) += max20730.o > obj-$(CONFIG_SENSORS_MAX20751) += max20751.o > +obj-$(CONFIG_SENSORS_MAX20826) += max20826.o > obj-$(CONFIG_SENSORS_MAX20830) += max20830.o > obj-$(CONFIG_SENSORS_MAX20860A) += max20860a.o > obj-$(CONFIG_SENSORS_MAX31785) += max31785.o > diff --git a/drivers/hwmon/pmbus/max20826.c b/drivers/hwmon/pmbus/max20826.c > new file mode 100644 > index 000000000000..1b481cdd8c93 > --- /dev/null > +++ b/drivers/hwmon/pmbus/max20826.c > @@ -0,0 +1,1037 @@ > +// SPDX-License-Identifier: GPL-2.0-only > +/* > + * Hardware monitoring driver for Analog Devices MAX20826 PMBus device > + * > + * Copyright 2026 Analog Devices Inc. > + */ > +#include <linux/bitfield.h> > +#include <linux/bitops.h> > +#include <linux/bits.h> > +#include <linux/cleanup.h> > +#include <linux/device.h> > +#include <linux/err.h> > +#include <linux/hwmon-sysfs.h> > +#include <linux/i2c.h> > +#include <linux/gpio/consumer.h> > +#include <linux/module.h> > +#include <linux/mod_devicetable.h> > +#include <linux/pmbus.h> > +#include <linux/property.h> > +#include <linux/regulator/driver.h> > +#include <linux/slab.h> > +#include <linux/sysfs.h> > +#include <linux/string.h> > +#include <linux/types.h> > +#include <linux/unaligned.h> > + > +#include "pmbus.h" > + > +#define MAX20826_REG_RAIL_PHASE_CFG 0xB1 > +#define MAX20826_REG_CTRL_MISC 0xCB > +#define MAX20826_OPL_EN_MSK BIT(7) > + > +#define MAX20826_REG_VOUT_RES 0xDB > +#define MAX20826_VOUT_RES_MSK BIT(7) > + > +#define MAX20855B_REG_VOUT_VRM 0xD1 > +#define MAX20855B_REG_VOUT_VRM_MASK BIT(4) > + > +#define MAX20826_REG_C_MODEAB 0xDC > +/* Byte 1 bits 7 and 6*/ > +#define MAX20826_C_MODEAB_MASK GENMASK(15, 14) > + > +#define MAX20826_REG_ADDR_MODE 0xEC > +#define MAX20826_PAGE_MODE_MSK BIT(7) > +#define MAX20826_DIRECT_ADDR_MSK GENMASK(6, 0) > + > +#define MAX20826_REG_OVERRIDE 0xED > +#define MAX20826_OVERRIDE_MASK BIT(7) > + > +#define MAX20826_REG_PHASE_DETECT 0xF3 > +#define MAX20826_REG_PHASE_READ 0xF4 > + > +#define MAX20826_REG_STATUS_MON 0xF9 > +#define MAX20826_PHASES_NUM_MASK GENMASK(7, 3) > + > +#define MAX20826_MAX_PAGES 2 > +#define MAX20826_MAX_PHASES 16 > +#define MAX20826_PHASES_PER_PAGE 8 > +#define MAX20826_INTF_PWMVID 1 > +#define MAX20826_INTF_AVSBUS 3 > + > +#define MAX20855B_PHASES_NUM_MASK GENMASK(7, 4) > +#define MAX20855B_MAX_PHASES 8 > +#define MAX20908_MAX_PHASES 8 > +#define MAX20912_MAX_PHASES 12 > +#define MAX20916_MAX_PHASES 16 > + > +struct max20826_chip_info { > + const char *vendor_bus_name; > + u8 max_phases; > + unsigned int phase_num_mask; > + u8 start_index_iin; > + u8 start_index_iout; > + bool is_reg_addr_mode_block; > + bool is_vout_direct; > + bool select_vrm; > + bool has_avsbus; > + bool has_opl; > + u8 (*count_phases)(const u8 *config, int page); > +}; > + > +struct max20826 { > + const struct max20826_chip_info *chip_info; > + struct pmbus_driver_info info; > + struct i2c_client *client; > + /* RAIL-B direct mode */ > + struct i2c_client *client_b; > + struct i2c_client *curr_client; > + struct gpio_desc *avren; > + struct gpio_desc *bvren; > + bool vendor_bus; > + bool high_speed[MAX20826_MAX_PAGES]; > + bool on_off_ctrl[MAX20826_MAX_PAGES]; > + bool opl_enabled[MAX20826_MAX_PAGES]; > +}; > + > +static u8 __max20826_count_phases(const u8 *config, int page) > +{ > + if (page) > + return hweight8(config[4]); > + > + return hweight8(config[0]) + hweight8(config[1]) - > + hweight8(config[4]); > +} > + > +static u8 __max20855b_count_phases(const u8 *config, int page) > +{ > + if (page) > + return hweight8(config[3] & 0x3F); > + > + return hweight8(config[0]) + hweight8(config[1] & 0x0F) - > + hweight8(config[3] & 0x3F); > +} > + > +static u8 __max20908_count_phases(const u8 *config, int page) > +{ > + if (page) > + return hweight8(config[4]); > + > + return hweight8(config[0]) + hweight8(config[1] & 0xF0) - > + hweight8(config[4]); > +} > + > +static u8 __max20912_count_phases(const u8 *config, int page) > +{ > + if (page) > + return hweight8(config[4]); > + > + return hweight8(config[0]) + hweight8(config[1] & 0xFC) - > + hweight8(config[4]); > +} > + > +#define to_max20826(p) container_of(p, struct max20826, info) > + > +enum { > + RAIL_A, > + RAIL_B, > +}; > + > +static const struct regulator_desc __maybe_unused max20826_reg_desc[] = { > + PMBUS_REGULATOR("vout", 0), > + PMBUS_REGULATOR("vout", 1), > +}; > + > +static struct i2c_client *max20826_select_rail(struct max20826 *st, > + int page, bool probing) > +{ > + int ret; > + > + /* > + * If in direct mode and we want RAIL_B (page 1) just return client_b. > + * Otherwise, set the proper page (if page mode) and return RAIL_A. > + */ > + if (st->client_b) { > + /* if 0xff just return the last client */ > + if (page < 0) > + return st->curr_client; > + if (page) > + st->curr_client = st->client_b; > + else > + st->curr_client = st->client; > + > + return st->curr_client; > + } > + > + if (!probing) > + ret = pmbus_set_page(st->client, page, 0xff); > + else > + ret = i2c_smbus_write_byte_data(st->client, PMBUS_PAGE, page); > + if (ret < 0) > + return ERR_PTR(ret); > + > + return st->client; > +} > + > +static int max20826_update_byte_data_unsafe(const struct i2c_client *client, > + int reg, u8 mask, u8 value) > +{ > + int ret; > + > + ret = i2c_smbus_read_byte_data(client, reg); > + if (ret < 0) > + return ret; > + > + value = (ret & ~mask) | (value & mask); > + > + return i2c_smbus_write_byte_data(client, reg, value); > +} Why not use pmbus_update_byte_data() ? > + > +static int max20826_read_block_data_unsafe(const struct i2c_client *client, > + int reg, u8 *block, u8 size) > +{ > + u8 block_data[I2C_SMBUS_BLOCK_MAX]; > + int ret; > + > + ret = i2c_smbus_read_i2c_block_data(client, reg, size + 1, > + block_data); size can be up to I2C_SMBUS_BLOCK_MAX (32). With that, the above code reads up to 33 bytes, which would exceed the size of block_data[]. > + if (ret < 0) > + return ret; > + if (ret < size + 1) > + return -EIO; No, this is wrong. The function needs to return the length of the returned data, which can be up to <size> and does not have to be an exact match. > + > + /* byte 0 comes with the block length, discard it */ > + memcpy(block, block_data + 1, size); > + return size; This is expected to return the actual data size, i.e., the value in block_data[0]. > +} > + > +static int __max20826_read_block_data(struct max20826 *st, int page, > + int reg, u8 *block, u8 size) > +{ > + struct i2c_client *rail; > + > + rail = max20826_select_rail(st, page, false); > + if (IS_ERR(rail)) > + return PTR_ERR(rail); > + > + return max20826_read_block_data_unsafe(rail, reg, block, size); > +} > + > +static int __max20826_read_byte_data(struct max20826 *st, int page, int reg) > +{ > + struct i2c_client *rail; > + > + rail = max20826_select_rail(st, page, false); > + if (IS_ERR(rail)) > + return PTR_ERR(rail); > + > + return i2c_smbus_read_byte_data(rail, reg); > +} > + > +static int __max20826_write_byte_data(struct max20826 *st, int page, int reg, > + u8 value) > +{ > + struct i2c_client *rail; > + > + rail = max20826_select_rail(st, page, false); > + if (IS_ERR(rail)) > + return PTR_ERR(rail); > + > + return i2c_smbus_write_byte_data(rail, reg, value); > +} > + > +static int __max20826_read_word_data(struct max20826 *st, int page, int reg) > +{ > + struct i2c_client *rail; > + > + rail = max20826_select_rail(st, page, false); > + if (IS_ERR(rail)) > + return PTR_ERR(rail); > + > + return i2c_smbus_read_word_data(rail, reg); > +} > + > +static ssize_t max20826_high_speed_en_show(struct device *dev, > + struct device_attribute *devattr, > + char *buf) > +{ > + struct i2c_client *client = to_i2c_client(dev->parent); > + const struct pmbus_driver_info *info = pmbus_get_driver_info(client); > + unsigned int page = to_sensor_dev_attr(devattr)->index; > + struct max20826 *st = to_max20826(info); > + > + return sysfs_emit(buf, "%u\n", st->high_speed[page]); > +} > + > +static ssize_t max20826_high_speed_en_store(struct device *dev, > + struct device_attribute *devattr, > + const char *buf, size_t count) > +{ > + struct i2c_client *client = to_i2c_client(dev->parent); > + const struct pmbus_driver_info *info = pmbus_get_driver_info(client); > + unsigned int page = to_sensor_dev_attr(devattr)->index; > + struct max20826 *st = to_max20826(info); > + struct i2c_client *rail; > + bool high_speed; > + int ret; > + > + ret = kstrtobool(buf, &high_speed); > + if (ret) > + return ret; > + > + guard(pmbus_lock)(client); > + > + rail = max20826_select_rail(st, page, false); > + if (IS_ERR(rail)) > + return PTR_ERR(rail); > + > + if (!high_speed) { > + if (!st->vendor_bus && st->chip_info->has_avsbus) { > + ret = max20826_update_byte_data_unsafe(rail, > + PMBUS_OPERATION, > + PB_OPERATION_CONTROL_V_SRC, 0); > + if (ret) > + return ret; > + > + goto out_success; > + } > + > + ret = max20826_update_byte_data_unsafe(rail, > + MAX20826_REG_OVERRIDE, > + MAX20826_OVERRIDE_MASK, > + FIELD_PREP(MAX20826_OVERRIDE_MASK, 1)); > + if (ret) > + return ret; > + > + goto out_success; > + } > + > + if (!st->vendor_bus) { > + ret = max20826_update_byte_data_unsafe(rail, PMBUS_OPERATION, > + PB_OPERATION_CONTROL_V_SRC, > + FIELD_PREP(PB_OPERATION_CONTROL_V_SRC, 3)); > + if (ret) > + return ret; > + > + goto out_success; > + } > + > + ret = max20826_update_byte_data_unsafe(rail, MAX20826_REG_OVERRIDE, > + MAX20826_OVERRIDE_MASK, 0); > + if (ret) > + return ret; > + > +out_success: > + st->high_speed[page] = high_speed; > + return count; > +} > + > +static ssize_t max20826_high_speed_bus_show(struct device *dev, > + struct device_attribute *devattr, > + char *buf) > +{ > + struct i2c_client *client = to_i2c_client(dev->parent); > + const struct pmbus_driver_info *info = pmbus_get_driver_info(client); > + struct max20826 *st = to_max20826(info); > + > + if (st->vendor_bus) > + return sysfs_emit(buf, "%s\n", st->chip_info->vendor_bus_name); > + > + return sysfs_emit(buf, "AVSBus\n"); > +} > + > +static SENSOR_DEVICE_ATTR_RW(in2_high_speed_en, max20826_high_speed_en, 0); > +static SENSOR_DEVICE_ATTR_RW(in3_high_speed_en, max20826_high_speed_en, 1); > +static SENSOR_DEVICE_ATTR_RO(in_high_speed_bus, max20826_high_speed_bus, 0); Those don't look like the values should or would be changed on the fly. Should that be devicetree properties ? > + > +static int max20826_read_curr(struct max20826 *st, int page, int phase, int reg) > +{ > + u8 val_out[6], start_byte; > + struct i2c_client *rail; > + int ret; > + > + if (phase == 0xff) > + return __max20826_read_word_data(st, page, reg); > + > + /* > + * On Rail_A phases are ascending (from 1) while on RAIL_B they > + * are descending (from 16). > + */ > + if (!page) > + phase += 1; > + else > + phase = st->chip_info->max_phases - phase; > + > + /* phase detect and read are only available on RAIL_A */ > + rail = max20826_select_rail(st, RAIL_A, false); > + if (IS_ERR(rail)) > + return PTR_ERR(rail); > + > + ret = i2c_smbus_write_byte_data(rail, MAX20826_REG_PHASE_DETECT, phase); > + if (ret < 0) > + return ret; > + > + /* > + * For MAX20826: Byte 2:3 is for phase IOUT 4:5 is for phase IIN. > + * For other chips: Byte 0:1 is for phase IOUT 2:3 is for phase IIN. > + */ > + if (reg == PMBUS_READ_IIN) > + start_byte = st->chip_info->start_index_iin; > + else > + start_byte = st->chip_info->start_index_iout; > + > + ret = max20826_read_block_data_unsafe(rail, MAX20826_REG_PHASE_READ, > + val_out, sizeof(val_out)); > + if (ret < 0) > + return ret; > + > + return get_unaligned_le16(&val_out[start_byte]); > +} > + > +static int max20826_read_word_data(struct i2c_client *client, int page, > + int phase, int reg) > +{ > + const struct pmbus_driver_info *info = pmbus_get_driver_info(client); > + struct max20826 *st = to_max20826(info); > + > + switch (reg) { > + case PMBUS_READ_IIN: > + case PMBUS_READ_IOUT: > + return max20826_read_curr(st, page, phase, reg); > + case PMBUS_IOUT_OC_FAULT_LIMIT: > + if (!st->chip_info->has_opl || !st->opl_enabled[page]) > + return __max20826_read_word_data(st, page, reg); > + return -EIO; > + case PMBUS_POUT_OP_FAULT_LIMIT: > + /* > + * If Over Power Limit is enabled, PMBUS_IOUT_OC_FAULT_LIMIT > + * shows the power limit and hence we need to report it > + * properly in Watts. > + */ > + if (!st->chip_info->has_opl) > + return __max20826_read_word_data(st, page, reg); > + > + if (st->opl_enabled[page]) > + return __max20826_read_word_data(st, page, > + PMBUS_IOUT_OC_FAULT_LIMIT); > + return -EIO; > + default: > + if (reg >= PMBUS_VIRT_BASE) > + return -EOPNOTSUPP; > + return __max20826_read_word_data(st, page, reg); > + } > +} > + > +static int max20826_write_word_data(struct i2c_client *client, int page, > + int reg, u16 word) > +{ > + const struct pmbus_driver_info *info = pmbus_get_driver_info(client); > + struct max20826 *st = to_max20826(info); > + struct i2c_client *rail; > + > + rail = max20826_select_rail(st, page, false); > + if (IS_ERR(rail)) > + return PTR_ERR(rail); > + > + return i2c_smbus_write_word_data(rail, reg, word); > +} > + > +static int max20826_regulator_enable(struct max20826 *st, int page, u8 byte) > +{ > + struct i2c_client *rail; > + > + rail = max20826_select_rail(st, page, false); > + if (IS_ERR(rail)) > + return PTR_ERR(rail); > + > + /* > + * If AVSBus is enabled (bits 5 and 4 set) the device refuses to set bit > + * 7 of the OPERATION register. Hence, to workaround this, we first > + * clear the bits and then set them all together. > + */ > + if (st->chip_info->has_avsbus && !st->vendor_bus && st->high_speed[page] && > + PB_OPERATION_CONTROL_ON & byte) { > + u8 __byte = byte & ~(PB_OPERATION_CONTROL_V_SRC | PB_OPERATION_CONTROL_ON); > + int ret; > + > + ret = i2c_smbus_write_byte_data(rail, PMBUS_OPERATION, __byte); > + if (ret < 0) > + return ret; > + } > + > + if (page) > + gpiod_set_value_cansleep(st->bvren, !!(PB_OPERATION_CONTROL_ON & byte)); > + else > + gpiod_set_value_cansleep(st->avren, !!(PB_OPERATION_CONTROL_ON & byte)); > + > + return i2c_smbus_write_byte_data(rail, PMBUS_OPERATION, byte); > +} > + > +static int max20826_write_byte_data(struct i2c_client *client, int page, > + int reg, u8 byte) > +{ > + const struct pmbus_driver_info *info = pmbus_get_driver_info(client); > + struct max20826 *st = to_max20826(info); > + > + switch (reg) { > + case PMBUS_OPERATION: > + return max20826_regulator_enable(st, page, byte); > + default: > + return __max20826_write_byte_data(st, page, reg, byte); > + } > +} > + > +static int max20826_write_byte(struct i2c_client *client, int page, u8 byte) > +{ > + const struct pmbus_driver_info *info = pmbus_get_driver_info(client); > + struct max20826 *st = to_max20826(info); > + struct i2c_client *rail; > + > + rail = max20826_select_rail(st, page, false); > + if (IS_ERR(rail)) > + return PTR_ERR(rail); > + > + return i2c_smbus_write_byte(rail, byte); > +} > + > +static int max20826_regulator_enabled(struct max20826 *st, int page) > +{ > + struct gpio_desc *gpio = page ? st->bvren : st->avren; > + int on, ret; > + > + if (gpio) { > + on = gpiod_get_value_cansleep(gpio); > + if (on < 0) > + return on; > + } else { > + /* If the gpios are not given, just assume it's on */ > + on = 1; > + } > + > + ret = __max20826_read_byte_data(st, page, PMBUS_OPERATION); > + if (ret < 0) > + return ret; > + > + if (st->on_off_ctrl[page]) > + on = (PB_OPERATION_CONTROL_ON & ret) && on; > + > + ret &= ~PB_OPERATION_CONTROL_ON; > + return ret | FIELD_PREP(PB_OPERATION_CONTROL_ON, on); > +} > + > +static int max20826_iout_status(struct max20826 *st, int page) > +{ > + int status; > + > + status = __max20826_read_byte_data(st, page, PMBUS_STATUS_IOUT); > + if (status < 0 || !st->chip_info->has_opl) > + return status; > + > + /* > + * If Over power limit is on, the fault condition is still set on the OC > + * bit > + */ > + if (st->opl_enabled[page] && (status & PB_IOUT_OC_FAULT)) > + return status | PB_POUT_OP_FAULT; > + > + return status; > +} > + > +static int max20826_read_byte_data(struct i2c_client *client, int page, int reg) > +{ > + const struct pmbus_driver_info *info = pmbus_get_driver_info(client); > + struct max20826 *st = to_max20826(info); > + > + switch (reg) { > + case PMBUS_OPERATION: > + return max20826_regulator_enabled(st, page); > + case PMBUS_STATUS_IOUT: > + return max20826_iout_status(st, page); > + default: > + return __max20826_read_byte_data(st, page, reg); > + } > +} > + > +static int max20826_read_block_data(struct i2c_client *client, int page, u8 reg, > + char *data_buf) > +{ > + const struct pmbus_driver_info *info = pmbus_get_driver_info(client); > + struct max20826 *st = to_max20826(info); > + > + switch (reg) { > + case PMBUS_MFR_ID: > + case PMBUS_MFR_MODEL: > + return __max20826_read_block_data(st, page, reg, data_buf, 16); > + case PMBUS_MFR_REVISION: > + return __max20826_read_block_data(st, page, reg, data_buf, 2); > + case PMBUS_MFR_DATE: > + return __max20826_read_block_data(st, page, reg, data_buf, 8); > + default: > + return -EOPNOTSUPP; > + } > +} > + > +static struct pmbus_driver_info max20826_default_info = { > + .pages = 1, > + .func[0] = PMBUS_HAVE_VIN | PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT | > + PMBUS_HAVE_IIN | PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT | > + PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP | > + PMBUS_HAVE_STATUS_INPUT | PMBUS_HAVE_PIN | PMBUS_HAVE_POUT | > + PMBUS_PHASE_VIRTUAL, > + .format[PSC_VOLTAGE_IN] = linear, > + .format[PSC_VOLTAGE_OUT] = direct, > + .format[PSC_TEMPERATURE] = linear, > + .format[PSC_CURRENT_IN] = linear, > + .format[PSC_CURRENT_OUT] = linear, > + .format[PSC_POWER] = linear, > + .m[PSC_VOLTAGE_OUT] = 2, > + .R[PSC_VOLTAGE_OUT] = 3, > + .write_byte_data = max20826_write_byte_data, > + .write_byte = max20826_write_byte, > + .read_byte_data = max20826_read_byte_data, > + .read_word_data = max20826_read_word_data, > + .write_word_data = max20826_write_word_data, > + .read_block_data = max20826_read_block_data, > +#if IS_ENABLED(CONFIG_SENSORS_MAX20826_REGULATOR) > + .num_regulators = 1, > + .reg_desc = max20826_reg_desc, > +#endif > +}; > + > +static struct attribute *max20826_attrs[MAX20826_MAX_PAGES + 1] = { > + &sensor_dev_attr_in2_high_speed_en.dev_attr.attr, > + &sensor_dev_attr_in3_high_speed_en.dev_attr.attr, > + &sensor_dev_attr_in_high_speed_bus.dev_attr.attr, > +}; > + > +static int max20826_detect_addr_mode(struct max20826 *st) > +{ > + struct device *dev = &st->client->dev; > + int ret, val; > + u8 val_buf[3]; > + > + /* > + * The idea is that after a POR, we are in page0 in which case we can > + * read MAX20826_REG_ADDR_MODE. If we fail to read > + * MAX20826_REG_ADDR_MODE, it might be due to a soft reset or unbinding > + * the device in which case the device could be left in page 1. Hence, > + * let's just try to change to page 0 and error out if we can't. > + * > + * OTOH, if the addressing mode is direct, we should be able to > + * read MAX20826_REG_ADDR_MODE. If not, we'll fail setting the page. > + * > + * REG_ADDR_MODE, MAX20855B has 6 bytes, other devices have 1. Byte 3 is > + * the equivalent of the single byte of the others. > + */ > + if (st->chip_info->is_reg_addr_mode_block) > + val = max20826_read_block_data_unsafe(st->client, > + MAX20826_REG_ADDR_MODE, > + val_buf, sizeof(val_buf)); > + else > + val = i2c_smbus_read_byte_data(st->client, MAX20826_REG_ADDR_MODE); > + > + if (val < 0) { > + ret = i2c_smbus_write_byte_data(st->client, PMBUS_PAGE, 0); > + if (ret < 0) > + return dev_err_probe(dev, ret, > + "Failed to change to page 0\n"); > + > + /* try again! */ > + if (st->chip_info->is_reg_addr_mode_block) > + val = max20826_read_block_data_unsafe(st->client, > + MAX20826_REG_ADDR_MODE, > + val_buf, sizeof(val_buf)); > + else > + val = i2c_smbus_read_byte_data(st->client, > + MAX20826_REG_ADDR_MODE); > + if (val < 0) > + return dev_err_probe(dev, val, > + "Failed to read MAX20826_REG_ADDR_MODE\n"); > + } > + > + if (st->chip_info->is_reg_addr_mode_block) > + val = val_buf[2]; > + > + if (val & MAX20826_PAGE_MODE_MSK) > + return 0; > + > + /* > + * If in direct mode, rail b will be accessible from the addr of RAIL_A > + * +1 > + */ > + st->client_b = devm_i2c_new_dummy_device(dev, st->client->adapter, > + st->client->addr + 1); > + if (IS_ERR(st->client_b)) > + return PTR_ERR(st->client_b); > + > + return 0; > +} > + > +static int max20826_detect_phases(struct max20826 *st, struct i2c_client *rail, > + struct pmbus_driver_info *info, u8 page, > + u8 expected) > +{ > + int ret, ret2 = 0, oper_save = -1; > + u8 status_mon[3]; > + u8 n_phases; > + > + ret = i2c_smbus_read_byte_data(rail, PMBUS_OPERATION); > + if (ret < 0) > + return ret; > + > + /* > + * In order to detect the number of phases, we need to be regulating. > + * It is also assumed we're already in the page we want to detect the > + * phases. > + */ > + if (!(ret & PB_OPERATION_CONTROL_ON)) { > + oper_save = ret; > + ret |= PB_OPERATION_CONTROL_ON; > + ret = i2c_smbus_write_byte_data(rail, PMBUS_OPERATION, ret); > + if (ret < 0) > + return ret; > + } > + > + ret = max20826_read_block_data_unsafe(rail, MAX20826_REG_STATUS_MON, > + status_mon, sizeof(status_mon)); > + if (ret < 0) > + goto out_restore_oper; > + > + n_phases = field_get(st->chip_info->phase_num_mask, status_mon[1]); > + if (n_phases != expected) { > + ret = dev_err_probe(&st->client->dev, -EIO, > + "Number of phases mismatch: expected=%u, detected=%u\n", > + expected, n_phases); > + goto out_restore_oper; > + } > + > + info->phases[page] = n_phases; > + for (unsigned int phase = 0; phase < n_phases; phase++) > + info->pfunc[phase] = PMBUS_HAVE_IOUT | PMBUS_HAVE_IIN; > + > +out_restore_oper: > + if (oper_save >= 0) > + ret2 = i2c_smbus_write_byte_data(rail, PMBUS_OPERATION, > + oper_save); > + > + return ret < 0 ? ret : ret2; > +} > + > +static int max20826_get_rail_config(struct max20826 *st, > + struct i2c_client *rail, u8 page) > +{ > + u8 ctrl_misc[2]; > + int ret; > + > + /* > + * Check if we need to control PMBUS_OPERATION in addition to the > + * CONTROL pin. > + */ > + ret = i2c_smbus_read_byte_data(rail, PMBUS_ON_OFF_CONFIG); > + if (ret < 0) > + return ret; > + > + st->on_off_ctrl[page] = !!(ret & PB_ON_OFF_CONFIG_OPERATION_REQ); > + > + /* > + * See if Over Power Limit is enabled. This will impact how > + * OC_FAULT_LIMIT is handled. > + */ > + if (st->chip_info->has_opl) { > + ret = max20826_read_block_data_unsafe(rail, MAX20826_REG_CTRL_MISC, > + ctrl_misc, sizeof(ctrl_misc)); > + if (ret < 0) > + return ret; > + > + st->opl_enabled[page] = !!(ctrl_misc[0] & MAX20826_OPL_EN_MSK); > + } > + > + if (st->vendor_bus) { > + ret = i2c_smbus_read_byte_data(rail, MAX20826_REG_OVERRIDE); > + if (ret < 0) > + return ret; > + > + st->high_speed[page] = !(ret & MAX20826_OVERRIDE_MASK); > + return 0; > + } > + > + ret = i2c_smbus_read_byte_data(rail, PMBUS_OPERATION); > + if (ret < 0) > + return ret; > + > + if (FIELD_GET(PB_OPERATION_CONTROL_V_SRC, ret) == MAX20826_INTF_AVSBUS) > + st->high_speed[page] = true; > + > + return 0; > +} > + > +static int max20826_add_attrs(struct device *dev, > + struct pmbus_driver_info *info) > +{ > + unsigned int attr, last = ARRAY_SIZE(max20826_attrs) - 1; > + struct attribute_group *group; > + > + info->groups = devm_kcalloc(dev, 2, sizeof(void *), GFP_KERNEL); > + if (!info->groups) > + return -ENOMEM; > + > + group = devm_kzalloc(dev, sizeof(*group), GFP_KERNEL); > + if (!group) > + return -ENOMEM; > + > + info->groups[0] = group; > + > + group->attrs = devm_kcalloc(dev, info->pages + 2, sizeof(void *), > + GFP_KERNEL); > + if (!group->attrs) > + return -ENOMEM; > + > + for (attr = 0; attr < info->pages; attr++) > + group->attrs[attr] = max20826_attrs[attr]; > + > + group->attrs[attr] = max20826_attrs[last]; > + > + return 0; > +} > + > +static int max20826_setup_vout_format(struct max20826 *st) > +{ > + struct pmbus_driver_info *info = &st->info; > + int ret; > + > + if (st->chip_info->is_vout_direct) { > + u8 vout_res[2]; > + > + ret = __max20826_read_block_data(st, RAIL_A, > + MAX20826_REG_VOUT_RES, > + vout_res, sizeof(vout_res)); > + if (ret < 0) > + return ret; > + > + /* check for 1 mv/LSB */ > + if (MAX20826_VOUT_RES_MSK & vout_res[1]) > + info->m[PSC_VOLTAGE_OUT] = 1; > + > + return 0; > + } > + > + st->info.format[PSC_VOLTAGE_OUT] = vid; > + for (unsigned int page = 0; page < info->pages; page++) { > + if (!st->chip_info->select_vrm) { > + /* only vr12 in this case */ > + st->info.vrm_version[page] = vr12; > + continue; > + } > + > + ret = __max20826_read_byte_data(st, page, > + MAX20855B_REG_VOUT_VRM); > + if (ret < 0) > + return ret; > + > + if (ret & MAX20855B_REG_VOUT_VRM_MASK) > + st->info.vrm_version[page] = vr13; > + else > + st->info.vrm_version[page] = vr12; > + } > + > + return 0; > +} > + > +static int max20826_setup(struct max20826 *st) > +{ > + struct pmbus_driver_info *info = &st->info; > + u8 config[5], expected_phases; > + struct device *dev = &st->client->dev; > + struct i2c_client *rail; > + int ret; > + > + ret = max20826_detect_addr_mode(st); > + if (ret < 0) > + return ret; > + > + if (st->chip_info->has_avsbus) { > + /* > + * After max20826_detect_addr_mode() we can just use RAIL_A client for > + * the following operations as even if in page mode, we must be in > + * page 0 by now. We also just need to check this for chips that might > + * also support selecting between custom or AVSBus. > + */ > + ret = i2c_smbus_read_word_data(st->client, MAX20826_REG_C_MODEAB); > + if (ret < 0) > + return ret; > + > + if (FIELD_GET(MAX20826_C_MODEAB_MASK, ret) == MAX20826_INTF_PWMVID) > + st->vendor_bus = true; > + } else { > + st->vendor_bus = true; > + } > + > + ret = max20826_read_block_data_unsafe(st->client, > + MAX20826_REG_RAIL_PHASE_CFG, > + config, sizeof(config)); > + if (ret < 0) > + return ret; > + > + /* > + * If we have phases in RAIL_B, we need to subtract them on config[1] as > + * those bits are also set in case RAIL_B has phases. > + */ > + expected_phases = st->chip_info->count_phases(config, RAIL_A); > + ret = max20826_detect_phases(st, st->client, info, RAIL_A, > + expected_phases); > + if (ret < 0) > + return ret; > + > + ret = max20826_get_rail_config(st, st->client, RAIL_A); > + if (ret < 0) > + return ret; > + > + /* Let's see if RAIL_B is present */ > + rail = max20826_select_rail(st, RAIL_B, true); > + if (IS_ERR(rail)) > + return PTR_ERR(rail); > + > + /* Let's see if there's something on RAIL_B */ > + st->bvren = devm_gpiod_get_optional(dev, "bvren", GPIOD_OUT_HIGH); > + if (IS_ERR(st->bvren)) > + return PTR_ERR(st->bvren); > + > + expected_phases = st->chip_info->count_phases(config, RAIL_B); > + ret = max20826_detect_phases(st, rail, info, RAIL_B, expected_phases); > + if (ret < 0) > + return ret; > + > + if (info->phases[RAIL_B]) { > + info->pages = MAX20826_MAX_PAGES; > + info->func[RAIL_B] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT | > + PMBUS_HAVE_IIN | PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT | > + PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP | > + PMBUS_HAVE_STATUS_INPUT | PMBUS_HAVE_PIN | PMBUS_HAVE_POUT | > + PMBUS_PHASE_VIRTUAL; > + > + if (IS_ENABLED(CONFIG_SENSORS_MAX20826_REGULATOR)) > + info->num_regulators = 2; > + > + ret = max20826_get_rail_config(st, rail, RAIL_B); > + if (ret < 0) > + return ret; > + } > + > + ret = max20826_setup_vout_format(st); > + if (ret < 0) > + return ret; > + > + return max20826_add_attrs(&st->client->dev, info); > +} > + > +static int max20826_probe(struct i2c_client *client) > +{ > + struct max20826 *st; > + int ret; > + > + st = devm_kzalloc(&client->dev, sizeof(*st), GFP_KERNEL); > + if (!st) > + return -ENOMEM; > + > + st->client = client; > + memcpy(&st->info, &max20826_default_info, sizeof(st->info)); > + > + st->chip_info = i2c_get_match_data(client); > + if (!st->chip_info) > + return -EINVAL; > + > + st->avren = devm_gpiod_get_optional(&client->dev, "avren", GPIOD_OUT_HIGH); > + if (IS_ERR(st->avren)) > + return PTR_ERR(st->avren); > + > + ret = max20826_setup(st); > + if (ret) > + return ret; > + > + return pmbus_do_probe(client, &st->info); > +} > + > +static const struct max20826_chip_info chip_info_max20826 = { > + .vendor_bus_name = "Nvidia PWMVID", > + .max_phases = MAX20826_MAX_PHASES, > + .phase_num_mask = MAX20826_PHASES_NUM_MASK, > + .start_index_iin = 4, > + .start_index_iout = 2, > + .is_vout_direct = true, > + .has_avsbus = true, > + .has_opl = true, > + .count_phases = __max20826_count_phases, > +}; > + > +static const struct max20826_chip_info chip_info_max20855b = { > + .vendor_bus_name = "Intel SVID", > + .max_phases = MAX20855B_MAX_PHASES, > + .phase_num_mask = MAX20855B_PHASES_NUM_MASK, > + .start_index_iin = 2, > + .start_index_iout = 0, > + .is_reg_addr_mode_block = true, > + .select_vrm = true, > + .count_phases = __max20855b_count_phases, > +}; > + > +static const struct max20826_chip_info chip_info_max20908 = { > + .vendor_bus_name = "AMD SVI3", > + .max_phases = MAX20908_MAX_PHASES, > + .phase_num_mask = MAX20826_PHASES_NUM_MASK, > + .start_index_iin = 2, > + .start_index_iout = 0, > + .count_phases = __max20908_count_phases, > +}; > + > +static const struct max20826_chip_info chip_info_max20912 = { > + .vendor_bus_name = "AMD SVI3", > + .max_phases = MAX20912_MAX_PHASES, > + .phase_num_mask = MAX20826_PHASES_NUM_MASK, > + .start_index_iin = 2, > + .start_index_iout = 0, > + .count_phases = __max20912_count_phases, > +}; > + > +static const struct max20826_chip_info chip_info_max20916 = { > + .vendor_bus_name = "AMD SVI3", > + .max_phases = MAX20916_MAX_PHASES, > + .phase_num_mask = MAX20826_PHASES_NUM_MASK, > + .start_index_iin = 2, > + .start_index_iout = 0, > + .count_phases = __max20826_count_phases, > +}; > + > +static const struct i2c_device_id max20826_id[] = { > + { "max20826", (kernel_ulong_t)&chip_info_max20826 }, > + { "max20855b", (kernel_ulong_t)&chip_info_max20855b }, > + { "max20908", (kernel_ulong_t)&chip_info_max20908 }, > + { "max20912", (kernel_ulong_t)&chip_info_max20912 }, > + { "max20916", (kernel_ulong_t)&chip_info_max20916 }, > + { } > +}; > +MODULE_DEVICE_TABLE(i2c, max20826_id); > + > +static const struct of_device_id max20826_of_match[] = { > + { .compatible = "adi,max20826", .data = &chip_info_max20826 }, > + { .compatible = "adi,max20855b", .data = &chip_info_max20855b }, > + { .compatible = "adi,max20908", .data = &chip_info_max20908 }, > + { .compatible = "adi,max20912", .data = &chip_info_max20912 }, > + { .compatible = "adi,max20916", .data = &chip_info_max20916 }, > + { } > +}; > +MODULE_DEVICE_TABLE(of, max20826_of_match); > + > +static struct i2c_driver max20826_driver = { > + .driver = { > + .name = "max20826", > + .of_match_table = max20826_of_match, > + }, > + .probe = max20826_probe, > + .id_table = max20826_id, > +}; > +module_i2c_driver(max20826_driver); > + > +MODULE_AUTHOR("Nuno Sá <[email protected]>"); > +MODULE_DESCRIPTION("PMBus driver for MAX20826"); > +MODULE_LICENSE("GPL"); > +MODULE_IMPORT_NS("PMBUS"); >