diff mbox series

[v3] regulator (max5970): Add hwmon support

Message ID 20230911113647.1259204-1-naresh.solanki@9elements.com (mailing list archive)
State Changes Requested
Headers show
Series [v3] regulator (max5970): Add hwmon support | expand

Commit Message

Naresh Solanki Sept. 11, 2023, 11:36 a.m. UTC
Utilize the integrated 10-bit ADC in Max5970/Max5978 to enable voltage
and current monitoring. This feature is seamlessly integrated through
the hwmon subsystem.

Signed-off-by: Naresh Solanki <naresh.solanki@9elements.com>
---
Changes in V3:
- Update signed-off
- Add break
- Update hwmon dev register name to max5970
- Remove changes in Kconfig.
Changes in V2:
- default case added for switch statement
- Add dependency on HWMON
---
 drivers/regulator/max5970-regulator.c | 126 ++++++++++++++++++++++++++
 1 file changed, 126 insertions(+)


base-commit: 41f02a383ac652f1a0b5538c5901b7ec93e37290

Comments

kernel test robot Sept. 13, 2023, 10:28 a.m. UTC | #1
Hi Naresh,

kernel test robot noticed the following build errors:

[auto build test ERROR on 41f02a383ac652f1a0b5538c5901b7ec93e37290]

url:    https://github.com/intel-lab-lkp/linux/commits/Naresh-Solanki/regulator-max5970-Add-hwmon-support/20230912-072321
base:   41f02a383ac652f1a0b5538c5901b7ec93e37290
patch link:    https://lore.kernel.org/r/20230911113647.1259204-1-naresh.solanki%409elements.com
patch subject: [PATCH v3] regulator (max5970): Add hwmon support
config: i386-randconfig-062-20230913 (https://download.01.org/0day-ci/archive/20230913/202309131847.4GIyTQIO-lkp@intel.com/config)
compiler: gcc-7 (Ubuntu 7.5.0-6ubuntu2) 7.5.0
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20230913/202309131847.4GIyTQIO-lkp@intel.com/reproduce)

If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202309131847.4GIyTQIO-lkp@intel.com/

All errors (new ones prefixed by >>):

   ld: drivers/regulator/max5970-regulator.o: in function `max597x_regulator_probe':
>> drivers/regulator/max5970-regulator.c:606: undefined reference to `devm_hwmon_device_register_with_info'


vim +606 drivers/regulator/max5970-regulator.c

   541	
   542	static int max597x_regulator_probe(struct platform_device *pdev)
   543	{
   544		struct max5970_data *max597x;
   545		struct regmap *regmap = dev_get_regmap(pdev->dev.parent, NULL);
   546		struct max5970_regulator *data;
   547		struct i2c_client *i2c = to_i2c_client(pdev->dev.parent);
   548		struct regulator_config config = { };
   549		struct regulator_dev *rdev;
   550		struct regulator_dev *rdevs[MAX5970_NUM_SWITCHES];
   551		struct device *hwmon_dev;
   552		int num_switches;
   553		int ret, i;
   554	
   555		if (!regmap)
   556			return -EPROBE_DEFER;
   557	
   558		max597x = devm_kzalloc(&i2c->dev, sizeof(struct max5970_data), GFP_KERNEL);
   559		if (!max597x)
   560			return -ENOMEM;
   561	
   562		i2c_set_clientdata(i2c, max597x);
   563	
   564		if (of_device_is_compatible(i2c->dev.of_node, "maxim,max5978"))
   565			max597x->num_switches = MAX5978_NUM_SWITCHES;
   566		else if (of_device_is_compatible(i2c->dev.of_node, "maxim,max5970"))
   567			max597x->num_switches = MAX5970_NUM_SWITCHES;
   568		else
   569			return -ENODEV;
   570	
   571		i2c_set_clientdata(i2c, max597x);
   572		num_switches = max597x->num_switches;
   573	
   574		for (i = 0; i < num_switches; i++) {
   575			data =
   576			    devm_kzalloc(&i2c->dev, sizeof(struct max5970_regulator),
   577					 GFP_KERNEL);
   578			if (!data)
   579				return -ENOMEM;
   580	
   581			data->num_switches = num_switches;
   582			data->regmap = regmap;
   583	
   584			ret = max597x_adc_range(regmap, i, &max597x->irng[i], &max597x->mon_rng[i]);
   585			if (ret < 0)
   586				return ret;
   587	
   588			data->irng = max597x->irng[i];
   589			data->mon_rng = max597x->mon_rng[i];
   590	
   591			config.dev = &i2c->dev;
   592			config.driver_data = (void *)data;
   593			config.regmap = data->regmap;
   594			rdev = devm_regulator_register(&i2c->dev,
   595						       &regulators[i], &config);
   596			if (IS_ERR(rdev)) {
   597				dev_err(&i2c->dev, "failed to register regulator %s\n",
   598					regulators[i].name);
   599				return PTR_ERR(rdev);
   600			}
   601			rdevs[i] = rdev;
   602			max597x->shunt_micro_ohms[i] = data->shunt_micro_ohms;
   603		}
   604	
   605		if (IS_ENABLED(CONFIG_HWMON)) {
 > 606			hwmon_dev = devm_hwmon_device_register_with_info(&i2c->dev, "max5970", max597x,
   607									 &max5970_chip_info, NULL);
   608			if (IS_ERR(hwmon_dev)) {
   609				return dev_err_probe(&i2c->dev, PTR_ERR(hwmon_dev),
   610						     "Unable to register hwmon device\n");
   611			}
   612		}
   613	
   614		if (i2c->irq) {
   615			ret =
   616			    max597x_setup_irq(&i2c->dev, i2c->irq, rdevs, num_switches,
   617					      data);
   618			if (ret) {
   619				dev_err(&i2c->dev, "IRQ setup failed");
   620				return ret;
   621			}
   622		}
   623	
   624		return ret;
   625	}
   626
diff mbox series

Patch

diff --git a/drivers/regulator/max5970-regulator.c b/drivers/regulator/max5970-regulator.c
index b56a174cde3d..208de66c0928 100644
--- a/drivers/regulator/max5970-regulator.c
+++ b/drivers/regulator/max5970-regulator.c
@@ -10,6 +10,7 @@ 
 #include <linux/bitops.h>
 #include <linux/device.h>
 #include <linux/err.h>
+#include <linux/hwmon.h>
 #include <linux/module.h>
 #include <linux/io.h>
 #include <linux/of.h>
@@ -32,6 +33,121 @@  enum max597x_regulator_id {
 	MAX597X_SW1,
 };
 
+static int max5970_read_adc(struct regmap *regmap, int reg, long *val)
+{
+	u8 reg_data[2];
+	int ret;
+
+	ret = regmap_bulk_read(regmap, reg, &reg_data[0], 2);
+	if (ret < 0)
+		return ret;
+
+	*val = (reg_data[0] << 2) | (reg_data[1] & 3);
+
+	return 0;
+}
+
+static int max5970_read(struct device *dev, enum hwmon_sensor_types type,
+			u32 attr, int channel, long *val)
+{
+	struct max5970_data *ddata = dev_get_drvdata(dev);
+	struct regmap *regmap = dev_get_regmap(dev->parent, NULL);
+	int ret;
+
+	switch (type) {
+	case hwmon_curr:
+		switch (attr) {
+		case hwmon_curr_input:
+			ret = max5970_read_adc(regmap, MAX5970_REG_CURRENT_H(channel), val);
+			/*
+			 * Calculate current from ADC value, IRNG range & shunt resistor value.
+			 * ddata->irng holds the voltage corresponding to the maximum value the
+			 * 10-bit ADC can measure.
+			 * To obtain the output, multiply the ADC value by the IRNG range (in
+			 * millivolts) and then divide it by the maximum value of the 10-bit ADC.
+			 */
+			*val = (*val * ddata->irng[channel]) >> 10;
+			/* Convert the voltage meansurement across shunt resistor to current */
+			*val = (*val * 1000) / ddata->shunt_micro_ohms[channel];
+			return ret;
+		default:
+			return -EOPNOTSUPP;
+		}
+
+	case hwmon_in:
+		switch (attr) {
+		case hwmon_in_input:
+			ret = max5970_read_adc(regmap, MAX5970_REG_VOLTAGE_H(channel), val);
+			/*
+			 * Calculate voltage from ADC value and MON range.
+			 * ddata->mon_rng holds the voltage corresponding to the maximum value the
+			 * 10-bit ADC can measure.
+			 * To obtain the output, multiply the ADC value by the MON range (in
+			 * microvolts) and then divide it by the maximum value of the 10-bit ADC.
+			 */
+			*val = mul_u64_u32_shr(*val, ddata->mon_rng[channel], 10);
+			/* uV to mV */
+			*val = *val / 1000;
+			return ret;
+		default:
+			return -EOPNOTSUPP;
+		}
+	default:
+		return -EOPNOTSUPP;
+	}
+}
+
+static umode_t max5970_is_visible(const void *data,
+				  enum hwmon_sensor_types type,
+				  u32 attr, int channel)
+{
+	struct max5970_data *ddata = (struct max5970_data *)data;
+
+	if (channel >= ddata->num_switches)
+		return 0;
+
+	switch (type) {
+	case hwmon_in:
+		switch (attr) {
+		case hwmon_in_input:
+			return 0444;
+		default:
+			break;
+		}
+		break;
+	case hwmon_curr:
+		switch (attr) {
+		case hwmon_curr_input:
+			/* Current measurement requires knowledge of the shunt resistor value. */
+			if (ddata->shunt_micro_ohms[channel])
+				return 0444;
+			break;
+		default:
+			break;
+		}
+		break;
+	default:
+		break;
+	}
+	return 0;
+}
+
+static const struct hwmon_ops max5970_hwmon_ops = {
+	.is_visible = max5970_is_visible,
+	.read = max5970_read,
+};
+
+static const struct hwmon_channel_info *max5970_info[] = {
+	HWMON_CHANNEL_INFO(in, HWMON_I_INPUT, HWMON_I_INPUT),
+	HWMON_CHANNEL_INFO(curr, HWMON_C_INPUT, HWMON_C_INPUT),
+	NULL
+};
+
+static const struct hwmon_chip_info max5970_chip_info = {
+	.ops = &max5970_hwmon_ops,
+	.info = max5970_info,
+};
+
 static int max597x_uvp_ovp_check_mode(struct regulator_dev *rdev, int severity)
 {
 	int ret, reg;
@@ -432,6 +548,7 @@  static int max597x_regulator_probe(struct platform_device *pdev)
 	struct regulator_config config = { };
 	struct regulator_dev *rdev;
 	struct regulator_dev *rdevs[MAX5970_NUM_SWITCHES];
+	struct device *hwmon_dev;
 	int num_switches;
 	int ret, i;
 
@@ -485,6 +602,15 @@  static int max597x_regulator_probe(struct platform_device *pdev)
 		max597x->shunt_micro_ohms[i] = data->shunt_micro_ohms;
 	}
 
+	if (IS_ENABLED(CONFIG_HWMON)) {
+		hwmon_dev = devm_hwmon_device_register_with_info(&i2c->dev, "max5970", max597x,
+								 &max5970_chip_info, NULL);
+		if (IS_ERR(hwmon_dev)) {
+			return dev_err_probe(&i2c->dev, PTR_ERR(hwmon_dev),
+					     "Unable to register hwmon device\n");
+		}
+	}
+
 	if (i2c->irq) {
 		ret =
 		    max597x_setup_irq(&i2c->dev, i2c->irq, rdevs, num_switches,