diff mbox series

hwmon: max34409 driver added

Message ID 20230215172613.359079-1-andybeg@gmail.com (mailing list archive)
State Changes Requested
Headers show
Series hwmon: max34409 driver added | expand

Commit Message

andybeg Feb. 15, 2023, 5:26 p.m. UTC
+MODULE_LICENSE("GPL");

Comments

Guenter Roeck Feb. 15, 2023, 7:34 p.m. UTC | #1
On Wed, Feb 15, 2023 at 08:26:12PM +0300, andybeg wrote:

Subject should be something like: "Add driver for MAX34409"

checkpatch reports:

total: 0 errors, 8 warnings, 12 checks, 256 lines checked

Please fix.

> 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@gagarin.me>
> +
> +
> +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

X -> 9

This driver for sure does not and never will cover all of MAX3440[0-9].

Same for the code itself.

> +	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
> +/*
> +*
> +*/

Completely useless comment.

> +
> +#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>

Alphabetic include file order, please.

> +
> +/*
> + * 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

No C++ comments, please.

> +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");

This is not an appropriate way to set limits or to provide scaling.
The driver should assume a default for Rsense (such as 1 mOhm),
and possibly provide the ability (via devicetree properties) to
configure actual rsense values. Anything else should be done using
sensors3.conf.

> +struct max3440x_data {
> +	struct i2c_client *client;
> +	struct device *hwmon_dev;
> +	const char *name;

'name' and 'hwmon_dev' are unused.

> +
> +   struct mutex update_lock;
> +   bool valid;
> +

valid is never read and pointless. 

Obviously some formatting problems. See checkpatch output.

> +	u16 adc[4];
> +	u8 oct[4];

'oct' is unused. I assume this is supposed to be for "Over Current Threshold",
which should be implemented as curr[1-4]_max sysfs attributes.

> +};
> +
> +static const char * const input_names[] = {
> +	[MAX3440X_ADC1]	=	"curr1",
> +	[MAX3440X_ADC2]	=	"curr2",
> +	[MAX3440X_ADC3]	=	"curr3",
> +	[MAX3440X_ADC4]	=	"curr4",

Those names have no value. It is obvious that curr1_input is curr1.

> +};
> +
> +static void max3440x_init_client(struct max3440x_data *data,
> +				struct i2c_client *client)
> +{
> +	u8 status;
> +   u16 val = 0;

Pointless initialization

> +	/*
> +	 * Start the conversions.
> +	 */

There is nothing in the datasheet suggesting that it would be necessary
to read the status register to start conversions.

> +	status = i2c_smbus_read_byte_data(client, MAX3440X_STATUS);

Please do not ignore errors. Anyway, "status" is neither used nor saved,
and reading it is thus just pointless.

> +
> +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);

This function for the most part duplicates max3440x_update_device()
and does not add any value.

> +}
> +
> +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);

i2c_smbus_read_byte_data() returns a negative error code. Please
do not ignore.

> +	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);

Reading all sensors to report one is a waste of i2c bandwidth. Please
drop max3440x_update_device() entirely and only read the data for the
sensor which is reported.

> +
> +	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);

Not that it matters because you should use the _with_info API, but the
last parameter of SENSOR_DEVICE_ATTR_{RO,RW} is supposed to be the index,
which is supposed to be used by the called function to determine
which of the sensors should be accessed. This means that separate functions
are not necessary.

> +
> +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);

Please rewrite to use devm_hwmon_device_register_with_info().

> +	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");

There is an emphasis on adc, not on hardware monitoring. The limit
registers / attributes are not implemented, and neither is status
reporting, making its value as hwmon driver quite limited.
Are you sure you want/need a hardware monitoring driver ? If this
is just used as an ADC and not as current monitor it may be better
to implement it as an IIO driver.

Guenter

> +MODULE_LICENSE("GPL");
> -- 
> 2.34.1
diff mbox series

Patch

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@gagarin.me>
+
+
+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");