]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
hwmon: (max77705) Add initial support
authorDzmitry Sankouski <dsankouski@gmail.com>
Wed, 23 Apr 2025 04:54:36 +0000 (07:54 +0300)
committerGuenter Roeck <linux@roeck-us.net>
Wed, 23 Apr 2025 14:21:15 +0000 (07:21 -0700)
Maxim MAX77705 is a Companion Power Management and Type-C interface IC.
It includes charger and fuel gauge blocks, and is capable of measuring
charger input current, system bus volatage and current, and bypass
voltage.

Add support for mentioned measurements.

Signed-off-by: Dzmitry Sankouski <dsankouski@gmail.com>
Link: https://lore.kernel.org/r/20250423-initial-support-for-max77705-sensors-v6-1-ff379e1b06c5@gmail.com
Signed-off-by: Guenter Roeck <linux@roeck-us.net>
Documentation/hwmon/index.rst
Documentation/hwmon/max77705.rst [new file with mode: 0644]
MAINTAINERS
drivers/hwmon/Kconfig
drivers/hwmon/Makefile
drivers/hwmon/max77705-hwmon.c [new file with mode: 0644]

index d17f942d8535359e942ae6359374431c1976afef..946b8a266d8940ee31aec48f0357e8f4901aeb19 100644 (file)
@@ -163,6 +163,7 @@ Hardware Monitoring Kernel Drivers
    max6639
    max6650
    max6697
+   max77705
    max8688
    mc13783-adc
    mc34vr500
diff --git a/Documentation/hwmon/max77705.rst b/Documentation/hwmon/max77705.rst
new file mode 100644 (file)
index 0000000..4a7680a
--- /dev/null
@@ -0,0 +1,39 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+Kernel driver max77705
+======================
+
+Supported chips:
+
+  * Maxim Integrated MAX77705
+
+    Prefix: 'max77705'
+
+    Addresses scanned: none
+
+    Datasheet: Not available
+
+Authors:
+      - Dzmitry Sankouski <dsankouski@gmail.com>
+
+Description
+-----------
+
+The MAX77705 PMIC provides current and voltage measurements besides fuelgauge:
+- chip input current
+- system bus current and voltage
+- VBYP voltage
+
+Sysfs Attributes
+----------------
+
+================= ========================================
+in1_label         "vbyp"
+in1_input         Measured chip vbyp voltage
+in2_label         "vsys"
+in2_input         Measured chip system bus voltage
+curr1_label       "iin"
+curr1_input       Measured chip input current.
+curr2_label       "isys"
+curr2_input       Measured chip system bus current.
+================= ========================================
index 6034c18b2c084c302ae8fb8199792ef607e45fa0..063c97f403105c86a0ed1f0d7edcfacab6c00471 100644 (file)
@@ -18433,6 +18433,13 @@ S:     Maintained
 F:     Documentation/hwmon/pc87427.rst
 F:     drivers/hwmon/pc87427.c
 
+MAX77705 HARDWARE MONITORING DRIVER
+M:     Dzmitry Sankouski <dsankouski@gmail.com>
+L:     linux-hwmon@vger.kernel.org
+S:     Maintained
+F:     Documentation/hwmon/max77705.rst
+F:     drivers/hwmon/max77705-hwmon.c
+
 PCA9532 LED DRIVER
 M:     Riku Voipio <riku.voipio@iki.fi>
 S:     Maintained
index 832d7e5f9f7b6c83c7d2a511698bd4df39651e80..80e277448c71bd04ebda9784e430b730a8bba3a0 100644 (file)
@@ -1318,6 +1318,15 @@ config SENSORS_MAX31790
          This driver can also be built as a module. If so, the module
          will be called max31790.
 
+config SENSORS_MAX77705
+       tristate "MAX77705 current and voltage sensor"
+       depends on MFD_MAX77705
+       help
+         If you say yes here you get support for MAX77705 sensors connected with I2C.
+
+         This driver can also be built as a module. If so, the module
+         will be called max77705-hwmon.
+
 config SENSORS_MC34VR500
        tristate "NXP MC34VR500 hardware monitoring driver"
        depends on I2C
index af18deb0422e5b7d89b8a1a4b2ccdf99e8674d7d..50df221f67c2fbeb8cf567839149da43d1b09cfb 100644 (file)
@@ -162,6 +162,7 @@ obj-$(CONFIG_SENSORS_MAX6650)       += max6650.o
 obj-$(CONFIG_SENSORS_MAX6697)  += max6697.o
 obj-$(CONFIG_SENSORS_MAX31790) += max31790.o
 obj-$(CONFIG_MAX31827) += max31827.o
+obj-$(CONFIG_SENSORS_MAX77705) += max77705-hwmon.o
 obj-$(CONFIG_SENSORS_MC13783_ADC)+= mc13783-adc.o
 obj-$(CONFIG_SENSORS_MC34VR500)        += mc34vr500.o
 obj-$(CONFIG_SENSORS_MCP3021)  += mcp3021.o
diff --git a/drivers/hwmon/max77705-hwmon.c b/drivers/hwmon/max77705-hwmon.c
new file mode 100644 (file)
index 0000000..990023e
--- /dev/null
@@ -0,0 +1,221 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ *  MAX77705 voltage and current hwmon driver.
+ *
+ *  Copyright (C) 2025 Dzmitry Sankouski <dsankouski@gmail.com>
+ */
+
+#include <linux/err.h>
+#include <linux/hwmon-sysfs.h>
+#include <linux/hwmon.h>
+#include <linux/kernel.h>
+#include <linux/mfd/max77705-private.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+
+struct channel_desc {
+       u8 reg;
+       u8 avg_reg;
+       const char *const label;
+       // register resolution. nano Volts for voltage, nano Amperes for current
+       u32 resolution;
+};
+
+static const struct channel_desc current_channel_desc[] = {
+       {
+               .reg = IIN_REG,
+               .label = "IIN_REG",
+               .resolution = 125000
+       },
+       {
+               .reg = ISYS_REG,
+               .avg_reg = AVGISYS_REG,
+               .label = "ISYS_REG",
+               .resolution = 312500
+       }
+};
+
+static const struct channel_desc voltage_channel_desc[] = {
+       {
+               .reg = VBYP_REG,
+               .label = "VBYP_REG",
+               .resolution = 427246
+       },
+       {
+               .reg = VSYS_REG,
+               .label = "VSYS_REG",
+               .resolution = 156250
+       }
+};
+
+static int max77705_read_and_convert(struct regmap *regmap, u8 reg, u32 res,
+                                    bool is_signed, long *val)
+{
+       int ret;
+       u32 regval;
+
+       ret = regmap_read(regmap, reg, &regval);
+       if (ret < 0)
+               return ret;
+
+       if (is_signed)
+               *val = mult_frac((long)sign_extend32(regval, 15), res, 1000000);
+       else
+               *val = mult_frac((long)regval, res, 1000000);
+
+       return 0;
+}
+
+static umode_t max77705_is_visible(const void *data,
+                                  enum hwmon_sensor_types type,
+                                  u32 attr, int channel)
+{
+       switch (type) {
+       case hwmon_in:
+               switch (attr) {
+               case hwmon_in_input:
+               case hwmon_in_label:
+                       return 0444;
+               default:
+                       break;
+               }
+               break;
+       case hwmon_curr:
+               switch (attr) {
+               case hwmon_curr_input:
+               case hwmon_in_label:
+                       return 0444;
+               case hwmon_curr_average:
+                       if (current_channel_desc[channel].avg_reg)
+                               return 0444;
+                       break;
+               default:
+                       break;
+               }
+               break;
+       default:
+               break;
+       }
+       return 0;
+}
+
+static int max77705_read_string(struct device *dev, enum hwmon_sensor_types type, u32 attr,
+                               int channel, const char **buf)
+{
+       switch (type) {
+       case hwmon_curr:
+               switch (attr) {
+               case hwmon_in_label:
+                       *buf = current_channel_desc[channel].label;
+                       return 0;
+               default:
+                       return -EOPNOTSUPP;
+               }
+
+       case hwmon_in:
+               switch (attr) {
+               case hwmon_in_label:
+                       *buf = voltage_channel_desc[channel].label;
+                       return 0;
+               default:
+                       return -EOPNOTSUPP;
+               }
+       default:
+               return -EOPNOTSUPP;
+       }
+}
+
+static int max77705_read(struct device *dev, enum hwmon_sensor_types type,
+                        u32 attr, int channel, long *val)
+{
+       struct regmap *regmap = dev_get_drvdata(dev);
+       u8 reg;
+       u32 res;
+
+       switch (type) {
+       case hwmon_curr:
+               switch (attr) {
+               case hwmon_curr_input:
+                       reg = current_channel_desc[channel].reg;
+                       res = current_channel_desc[channel].resolution;
+
+                       return max77705_read_and_convert(regmap, reg, res, true, val);
+               case hwmon_curr_average:
+                       reg = current_channel_desc[channel].avg_reg;
+                       res = current_channel_desc[channel].resolution;
+
+                       return max77705_read_and_convert(regmap, reg, res, true, val);
+               default:
+                       return -EOPNOTSUPP;
+               }
+
+       case hwmon_in:
+               switch (attr) {
+               case hwmon_in_input:
+                       reg = voltage_channel_desc[channel].reg;
+                       res = voltage_channel_desc[channel].resolution;
+
+                       return max77705_read_and_convert(regmap, reg, res, false, val);
+               default:
+                       return -EOPNOTSUPP;
+               }
+       default:
+               return -EOPNOTSUPP;
+       }
+
+       return 0;
+}
+
+static const struct hwmon_ops max77705_hwmon_ops = {
+       .is_visible = max77705_is_visible,
+       .read = max77705_read,
+       .read_string = max77705_read_string,
+};
+
+static const struct hwmon_channel_info *max77705_info[] = {
+       HWMON_CHANNEL_INFO(in,
+                          HWMON_I_INPUT | HWMON_I_LABEL,
+                          HWMON_I_INPUT | HWMON_I_LABEL
+                       ),
+       HWMON_CHANNEL_INFO(curr,
+                          HWMON_C_INPUT | HWMON_C_LABEL,
+                          HWMON_C_INPUT | HWMON_C_AVERAGE | HWMON_C_LABEL
+                       ),
+       NULL
+};
+
+static const struct hwmon_chip_info max77705_chip_info = {
+       .ops = &max77705_hwmon_ops,
+       .info = max77705_info,
+};
+
+static int max77705_hwmon_probe(struct platform_device *pdev)
+{
+       struct device *hwmon_dev;
+       struct regmap *regmap;
+
+       regmap = dev_get_regmap(pdev->dev.parent, NULL);
+       if (!regmap)
+               return -ENODEV;
+
+       hwmon_dev = devm_hwmon_device_register_with_info(&pdev->dev, "max77705", regmap,
+                                                        &max77705_chip_info, NULL);
+       if (IS_ERR(hwmon_dev))
+               return dev_err_probe(&pdev->dev, PTR_ERR(hwmon_dev),
+                               "Unable to register hwmon device\n");
+
+       return 0;
+};
+
+static struct platform_driver max77705_hwmon_driver = {
+       .driver = {
+               .name = "max77705-hwmon",
+       },
+       .probe = max77705_hwmon_probe,
+};
+
+module_platform_driver(max77705_hwmon_driver);
+
+MODULE_AUTHOR("Dzmitry Sankouski <dsankouski@gmail.com>");
+MODULE_DESCRIPTION("MAX77705 monitor driver");
+MODULE_LICENSE("GPL");