[PATCH] hwmon: max34409 driver added

From: andybeg
Date: Wed Feb 15 2023 - 12:26:53 EST


diff --git a/Documentation/hwmon/max34409.rst b/Documentation/hwmon/max34409.rst
new file mode 100644
index 000000000000..91779c6a9163
--- /dev/null
+++ b/Documentation/hwmon/max34409.rst
@@ -0,0 +1,23 @@
+Kernel driver max34409
+=====================
+
+Supported chips:
+ * Analog Devices MAX34409
+ Prefix: 'max34409'
+ Datasheet: https://www.analog.com/media/en/technical-documentation/data-sheets/MAX34408-MAX34409.pdf
+
+Author: Andrey Kononov <a.kononov@xxxxxxxxxx>
+
+
+Description
+-----------
+
+This driver for SMBus Dual/Quad Current Monitor MaximIntegrated MAX34409
+
+
+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.
diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
index 3176c33af6c6..de412f7dcad8 100644
--- a/drivers/hwmon/Kconfig
+++ b/drivers/hwmon/Kconfig
@@ -1088,6 +1088,13 @@ config SENSORS_MAX31760
This driver can also be built as a module. If so, the module
will be called max31760.

+config SENSORS_MAX3440X
+ tristate "Maxim max3440x SMBus Dual/Quad Current Monitor"
+ depends on I2C
+ help
+ Say yes here to build support for Maxim family of SMBus Dual/Quad Current Monitors.
+ This driver is mutually exclusive with the HWMON version.
+
config SENSORS_MAX6620
tristate "Maxim MAX6620 fan controller"
depends on I2C
diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile
index e2e4e87b282f..a4e24d2b03c1 100644
--- a/drivers/hwmon/Makefile
+++ b/drivers/hwmon/Makefile
@@ -141,6 +141,7 @@ obj-$(CONFIG_SENSORS_MAX197) += max197.o
obj-$(CONFIG_SENSORS_MAX31722) += max31722.o
obj-$(CONFIG_SENSORS_MAX31730) += max31730.o
obj-$(CONFIG_SENSORS_MAX31760) += max31760.o
+obj-$(CONFIG_SENSORS_MAX3440X) += max3440x.o
obj-$(CONFIG_SENSORS_MAX6620) += max6620.o
obj-$(CONFIG_SENSORS_MAX6621) += max6621.o
obj-$(CONFIG_SENSORS_MAX6639) += max6639.o
diff --git a/drivers/hwmon/max3440x.c b/drivers/hwmon/max3440x.c
new file mode 100644
index 000000000000..b62c34f9425c
--- /dev/null
+++ b/drivers/hwmon/max3440x.c
@@ -0,0 +1,213 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+*
+*/
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/jiffies.h>
+#include <linux/i2c.h>
+#include <linux/hwmon.h>
+#include <linux/hwmon-sysfs.h>
+#include <linux/err.h>
+#include <linux/mutex.h>
+#include <linux/sysfs.h>
+
+/*
+ * Registers description.
+ */
+#define MAX3440X_STATUS 0x00
+#define MAX3440X_CONTROL 0x01
+#define MAX3440X_OCDELAY 0x02
+#define MAX3440X_SDDELAY 0x03
+#define MAX3440X_ADC1 0x04 /* readonly */
+#define MAX3440X_ADC2 0x05 /* readonly */
+#define MAX3440X_ADC3 0x06 /* readonly */
+#define MAX3440X_ADC4 0x07 /* readonly */
+#define MAX3440X_OCT1 0x08
+#define MAX3440X_OCT2 0x09
+#define MAX3440X_OCT3 0x0A
+#define MAX3440X_OCT4 0x0B
+#define MAX3440X_DID 0x0C /* readonly */
+#define MAX3440X_DCYY 0x0D /* readonly */
+#define MAX3440X_DCWW 0x0E /* readonly */
+
+//maximal current in mA throw RSENSE, that can be measured. see datasheet table 18
+static unsigned short imax[4];
+module_param_array(imax, short, NULL, 0);
+MODULE_PARM_DESC(imax,
+ "maximal current in mA throw RSENSE, that can be measured. see datasheet table 18");
+struct max3440x_data {
+ struct i2c_client *client;
+ struct device *hwmon_dev;
+ const char *name;
+
+ struct mutex update_lock;
+ bool valid;
+
+ u16 adc[4];
+ u8 oct[4];
+};
+
+static const char * const input_names[] = {
+ [MAX3440X_ADC1] = "curr1",
+ [MAX3440X_ADC2] = "curr2",
+ [MAX3440X_ADC3] = "curr3",
+ [MAX3440X_ADC4] = "curr4",
+};
+
+static void max3440x_init_client(struct max3440x_data *data,
+ struct i2c_client *client)
+{
+ u8 status;
+ u16 val = 0;
+ /*
+ * Start the conversions.
+ */
+ status = i2c_smbus_read_byte_data(client, MAX3440X_STATUS);
+
+val = (u16)i2c_smbus_read_byte_data(client, MAX3440X_ADC1);
+ data->adc[0] = DIV_ROUND_CLOSEST((imax[0] * val), 256);
+ val = i2c_smbus_read_byte_data(client, MAX3440X_ADC2);
+ data->adc[1] = DIV_ROUND_CLOSEST((imax[1] * val), 256);
+ val = i2c_smbus_read_byte_data(client, MAX3440X_ADC3);
+ data->adc[2] = DIV_ROUND_CLOSEST((imax[2] * val), 256);
+ val = i2c_smbus_read_byte_data(client, MAX3440X_ADC4);
+ data->adc[3] = DIV_ROUND_CLOSEST((imax[3] * val), 256);
+}
+
+static struct max3440x_data *max3440x_update_device(struct device *dev)
+{
+ struct max3440x_data *data = dev_get_drvdata(dev);
+ struct i2c_client *client = data->client;
+ u16 val;
+
+ mutex_lock(&data->update_lock);
+
+ dev_dbg(dev, "Updating max3440 data.\n");
+ val = (u16)i2c_smbus_read_byte_data(client,
+ MAX3440X_ADC1);
+ data->adc[0] = DIV_ROUND_CLOSEST((imax[0] * val), 256);
+ val = (u16)i2c_smbus_read_byte_data(client,
+ MAX3440X_ADC2);
+ data->adc[1] = DIV_ROUND_CLOSEST((imax[1] * val), 256);
+ val = (u16)i2c_smbus_read_byte_data(client,
+ MAX3440X_ADC3);
+ data->adc[2] = DIV_ROUND_CLOSEST((imax[2] * val), 256);
+ val = (u16)i2c_smbus_read_byte_data(client,
+ MAX3440X_ADC4);
+ data->adc[3] = DIV_ROUND_CLOSEST((imax[3] * val), 256);
+
+ data->valid = 1;
+ mutex_unlock(&data->update_lock);
+
+ return data;
+}
+static ssize_t adc1_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct sensor_device_attribute *attr2 = to_sensor_dev_attr(attr);
+ struct max3440x_data *data = max3440x_update_device(dev);
+
+ return sprintf(buf, "%d\n", data->adc[0]);
+}
+static ssize_t adc2_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct sensor_device_attribute *attr2 = to_sensor_dev_attr(attr);
+ struct max3440x_data *data = max3440x_update_device(dev);
+
+ return sprintf(buf, "%d\n", data->adc[1]);
+}
+static ssize_t adc3_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct sensor_device_attribute *attr2 = to_sensor_dev_attr(attr);
+ struct max3440x_data *data = max3440x_update_device(dev);
+
+ return sprintf(buf, "%d\n", data->adc[2]);
+}
+static ssize_t adc4_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct sensor_device_attribute *attr2 = to_sensor_dev_attr(attr);
+ struct max3440x_data *data = max3440x_update_device(dev);
+
+ return sprintf(buf, "%d\n", data->adc[3]);
+}
+
+static ssize_t label_show(struct device *dev,
+ struct device_attribute *devattr, char *buf)
+{
+ return sprintf(buf, "%s\n",
+ input_names[to_sensor_dev_attr(devattr)->index]);
+}
+
+static SENSOR_DEVICE_ATTR_RO(curr1_input, adc1, 0);
+static SENSOR_DEVICE_ATTR_RO(curr1_label, label, MAX3440X_ADC1);
+static SENSOR_DEVICE_ATTR_RO(curr2_input, adc2, 0);
+static SENSOR_DEVICE_ATTR_RO(curr2_label, label, MAX3440X_ADC2);
+static SENSOR_DEVICE_ATTR_RO(curr3_input, adc3, 0);
+static SENSOR_DEVICE_ATTR_RO(curr3_label, label, MAX3440X_ADC3);
+static SENSOR_DEVICE_ATTR_RO(curr4_input, adc4, 0);
+static SENSOR_DEVICE_ATTR_RO(curr4_label, label, MAX3440X_ADC4);
+
+static struct attribute *max3440x_attrs[] = {
+ &sensor_dev_attr_curr1_input.dev_attr.attr,
+ &sensor_dev_attr_curr1_label.dev_attr.attr,
+ &sensor_dev_attr_curr2_input.dev_attr.attr,
+ &sensor_dev_attr_curr2_label.dev_attr.attr,
+ &sensor_dev_attr_curr3_input.dev_attr.attr,
+ &sensor_dev_attr_curr3_label.dev_attr.attr,
+ &sensor_dev_attr_curr4_input.dev_attr.attr,
+ &sensor_dev_attr_curr4_label.dev_attr.attr,
+ NULL
+};
+
+ATTRIBUTE_GROUPS(max3440x);
+
+static int max3440x_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct device *dev = &client->dev;
+ struct max3440x_data *data;
+ struct device *hwmon_dev;
+
+ data = devm_kzalloc(dev, sizeof(struct max3440x_data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ data->client = client;
+ mutex_init(&data->update_lock);
+
+ /* Initialize the MAX3440x chip */
+ max3440x_init_client(data, client);
+
+ hwmon_dev = devm_hwmon_device_register_with_groups(&client->dev,
+ client->name, data,
+ max3440x_groups);
+ return PTR_ERR_OR_ZERO(hwmon_dev);
+}
+
+
+static const struct i2c_device_id max3440x_id[] = {
+ { "max34409", 0 },
+ { }
+};
+
+MODULE_DEVICE_TABLE(i2c, max3440x_id);
+
+static const struct i2c_driver max3440x_driver = {
+ .class = I2C_CLASS_HWMON,
+ .driver = {
+ .name = "max3440x",
+ },
+ .probe = max3440x_probe,
+ .id_table = max3440x_id,
+};
+
+module_i2c_driver(max3440x_driver);
+
+MODULE_AUTHOR("Andrey Kononov");
+MODULE_DESCRIPTION("I2C adc driver");
+MODULE_LICENSE("GPL");
--
2.34.1