[2/2] hwmon: Add AMS AS6200 temperature sensor

Message ID 63e352150ed51eefce90ca4058af5459730174b2.1702744180.git.alkuor@gmail.com
State New
Headers
Series None |

Commit Message

Abdel Alkuor Dec. 16, 2023, 4:39 p.m. UTC
  as6200 is a temperature sensor with 0.0625°C resolution and a range between
-40°C to 125°C.

By default, the driver configures as6200 as following:
- Converstion rate: 8 Hz
- Conversion mode: continuous
- Consecutive fault counts: 6 samples
- Alert state: high polarity
- Alert mode: comparator mode

Interrupt is supported for the alert pin.

Datasheet: https://ams.com/documents/20143/36005/AS6200_DS000449_4-00.pdf
Signed-off-by: Abdel Alkuor <alkuor@gmail.com>
---
 Documentation/hwmon/as6200.rst |  50 +++++++
 drivers/hwmon/Kconfig          |  10 ++
 drivers/hwmon/Makefile         |   1 +
 drivers/hwmon/as6200.c         | 249 +++++++++++++++++++++++++++++++++
 4 files changed, 310 insertions(+)
 create mode 100644 Documentation/hwmon/as6200.rst
 create mode 100644 drivers/hwmon/as6200.c
  

Comments

Guenter Roeck Dec. 16, 2023, 6:46 p.m. UTC | #1
On 12/16/23 08:39, Abdel Alkuor wrote:
> as6200 is a temperature sensor with 0.0625°C resolution and a range between
> -40°C to 125°C.
> 
> By default, the driver configures as6200 as following:
> - Converstion rate: 8 Hz
> - Conversion mode: continuous
> - Consecutive fault counts: 6 samples
> - Alert state: high polarity
> - Alert mode: comparator mode
> 
> Interrupt is supported for the alert pin.
> 
> Datasheet: https://ams.com/documents/20143/36005/AS6200_DS000449_4-00.pdf
> Signed-off-by: Abdel Alkuor <alkuor@gmail.com>

Please explain why the lm75 driver would not work for this chip.
I don't immediately see the problem, especially with TMP112 using almost
the same configuration register layout.

Thanks,
Guenter
  
Abdel Alkuor Dec. 16, 2023, 10:07 p.m. UTC | #2
On Sat, Dec 16, 2023 at 10:46:53AM -0800, Guenter Roeck wrote:
> On 12/16/23 08:39, Abdel Alkuor wrote:
> Please explain why the lm75 driver would not work for this chip.
> I don't immediately see the problem, especially with TMP112 using almost
> the same configuration register layout.
> 
Hi Guenter,

That's a good point, tmp112 is very similar to as6200 except R0/R1 and
EM bits don't exist in as6200. That being said, the current config for
tmp112 in lm75 driver can be used for as6200 as the default R0/R1 is
set to 12bits which is the only resolution supported in as6200.

Should I use tmp112 params for as6200?

Also, can we add support for hwmon_temp_alarm and alert interrupt?

Thanks,
Abdel
  
Guenter Roeck Dec. 17, 2023, 1:40 a.m. UTC | #3
On 12/16/23 14:07, Abdel Alkuor wrote:
> On Sat, Dec 16, 2023 at 10:46:53AM -0800, Guenter Roeck wrote:
>> On 12/16/23 08:39, Abdel Alkuor wrote:
>> Please explain why the lm75 driver would not work for this chip.
>> I don't immediately see the problem, especially with TMP112 using almost
>> the same configuration register layout.
>>
> Hi Guenter,
> 
> That's a good point, tmp112 is very similar to as6200 except R0/R1 and
> EM bits don't exist in as6200. That being said, the current config for
> tmp112 in lm75 driver can be used for as6200 as the default R0/R1 is
> set to 12bits which is the only resolution supported in as6200.
> 
> Should I use tmp112 params for as6200?
> 

Sure, or just add a separate entry for as6200.

> Also, can we add support for hwmon_temp_alarm and alert interrupt?
> 
Yes, of course. Just make it conditional on chip types supporting it
(not necessarily all of them, just the ones you know about).

Thanks,
Guenter
  
Abdel Alkuor Dec. 17, 2023, 4:59 a.m. UTC | #4
On Sat, Dec 16, 2023 at 05:40:35PM -0800, Guenter Roeck wrote:
> On 12/16/23 14:07, Abdel Alkuor wrote:
> > On Sat, Dec 16, 2023 at 10:46:53AM -0800, Guenter Roeck wrote:
> > > On 12/16/23 08:39, Abdel Alkuor wrote:
> > Should I use tmp112 params for as6200?
> > 
> 
> Sure, or just add a separate entry for as6200.
>
I think some modifications need to be done regarding setting the default
configuration for chips with config reg of 16 bits.

Currently, tmp112 set_mask and clr_mask look like this

   [tmp112] = {
   	 .set_mask = 3 << 5,	/* 8 samples / second */
   	 .clr_mask = 1 << 7,	/* no one-shot mode*/
   	 ...
   }

and in probe function, we are using i2c_smbus_read_byte_data which
basically reads byte 1 of tmp112 config reg and in lm75_write_config
it writes byte 1 of tmp112 config reg. Now based on tmp112 set_mask,
we want to set the sample rate but we actually setting R0 and R1 instead.
According to tmp112 datasheet on pg. 16, byte 1 is written first then
byte 2, where byte 2 has the conversion rate at bit 6 and 7 (CR0/CR1).

tmp112 datasheet: https://www.ti.com/lit/ds/symlink/tmp112.pdf?ts=1702713491401&ref_url=https%253A%252F%252Fwww.google.com%252F

Now, to accommodate 16 bit config register read/write, something along these lines can
be done:
- In struct lm75_params,
  - change set_mask and clr_mask from u8 to u16 
  - Add config reg two bytes size flag
- Use the proper function to read the config reg based on config reg size i.e
  For one byte config reg, use i2c_smbus_read_byte_data, and for 2 bytes
  config reg, use regmap_read.

  static int lm75_probe(struct i2c_client *client)
  {
   	...
	if (data->params->config_reg_16bits)
  		status = regmap_read(client, LM75_REG_CONF, &regval);
		if (status < 0) {
			dev_dbg(dev, "Can't read config? %d\n", status);
			return status;
		}
		data->orig_conf = regval;
		data->current_conf = regval;
	} else {
  		status = i2c_smbus_read_byte_data(client, LM75_REG_CONF);
		if (status < 0) {
			dev_dbg(dev, "Can't read config? %d\n", status);
			return status;
		}
		data->orig_conf = status;
		data->current_conf = status;
	}
	...
   }

   static int lm75_write_config(struct lm75_data *data, u16 set_mask,
   			     u16 clr_mask)
   {
     
     if (data->params->config_reg_16bits)
     	clr_mask |= LM75_SHUTDOWN << 8;
     else
     	clr_mask |= LM75_SHUTDOWN;
     ... 
     	if (data->params->config_reg_16bits)
     		err = regmap_write(data->regmap, LM75_REG_CONF, value);
     	else
     		err = i2c_smbus_write_byte_data(data->client,
     			       			LM75_REG_CONF,
     						value);
     ...
   }

Based on that, the new tmp112 set_mask and clr_mask would look like this instead,
  [tmp112] = {
  	.set_mask = 3 << 6,	/* 8 samples / second */
  	.clr_mask = 1 << 15,	/* no one-shot mode*/
	.config_reg_16bits = 1,
  	...
  }

Thanks,
Abdel
  
Guenter Roeck Dec. 17, 2023, 6:06 a.m. UTC | #5
On 12/16/23 20:59, Abdel Alkuor wrote:
> On Sat, Dec 16, 2023 at 05:40:35PM -0800, Guenter Roeck wrote:
>> On 12/16/23 14:07, Abdel Alkuor wrote:
>>> On Sat, Dec 16, 2023 at 10:46:53AM -0800, Guenter Roeck wrote:
>>>> On 12/16/23 08:39, Abdel Alkuor wrote:
>>> Should I use tmp112 params for as6200?
>>>
>>
>> Sure, or just add a separate entry for as6200.
>>
> I think some modifications need to be done regarding setting the default
> configuration for chips with config reg of 16 bits.
> 
> Currently, tmp112 set_mask and clr_mask look like this
> 
>     [tmp112] = {
>     	 .set_mask = 3 << 5,	/* 8 samples / second */
>     	 .clr_mask = 1 << 7,	/* no one-shot mode*/
>     	 ...
>     }
> 
> and in probe function, we are using i2c_smbus_read_byte_data which
> basically reads byte 1 of tmp112 config reg and in lm75_write_config
> it writes byte 1 of tmp112 config reg. Now based on tmp112 set_mask,
> we want to set the sample rate but we actually setting R0 and R1 instead.
> According to tmp112 datasheet on pg. 16, byte 1 is written first then
> byte 2, where byte 2 has the conversion rate at bit 6 and 7 (CR0/CR1).
> 
> tmp112 datasheet: https://www.ti.com/lit/ds/symlink/tmp112.pdf?ts=1702713491401&ref_url=https%253A%252F%252Fwww.google.com%252F
> 
> Now, to accommodate 16 bit config register read/write, something along these lines can
> be done:
> - In struct lm75_params,
>    - change set_mask and clr_mask from u8 to u16
>    - Add config reg two bytes size flag
> - Use the proper function to read the config reg based on config reg size i.e
>    For one byte config reg, use i2c_smbus_read_byte_data, and for 2 bytes
>    config reg, use regmap_read.
> 
>    static int lm75_probe(struct i2c_client *client)
>    {
>     	...
> 	if (data->params->config_reg_16bits)
>    		status = regmap_read(client, LM75_REG_CONF, &regval);
> 		if (status < 0) {
> 			dev_dbg(dev, "Can't read config? %d\n", status);
> 			return status;
> 		}
> 		data->orig_conf = regval;
> 		data->current_conf = regval;
> 	} else {
>    		status = i2c_smbus_read_byte_data(client, LM75_REG_CONF);
> 		if (status < 0) {
> 			dev_dbg(dev, "Can't read config? %d\n", status);
> 			return status;
> 		}
> 		data->orig_conf = status;
> 		data->current_conf = status;
> 	}
> 	...
>     }
> 
>     static int lm75_write_config(struct lm75_data *data, u16 set_mask,
>     			     u16 clr_mask)
>     {
>       
>       if (data->params->config_reg_16bits)
>       	clr_mask |= LM75_SHUTDOWN << 8;
>       else
>       	clr_mask |= LM75_SHUTDOWN;
>       ...
>       	if (data->params->config_reg_16bits)
>       		err = regmap_write(data->regmap, LM75_REG_CONF, value);
>       	else
>       		err = i2c_smbus_write_byte_data(data->client,
>       			       			LM75_REG_CONF,
>       						value);
>       ...
>     }
> 
> Based on that, the new tmp112 set_mask and clr_mask would look like this instead,
>    [tmp112] = {
>    	.set_mask = 3 << 6,	/* 8 samples / second */
>    	.clr_mask = 1 << 15,	/* no one-shot mode*/
> 	.config_reg_16bits = 1,
>    	...
>    }
> 

Yes, you are correct, we'll need something like that. lm75_update_interval()
tries to solve the problem for tmp112, but that doesn't work with
set_mask/clear_mask. We should have a separate function lm75_read_config(),
though, to hide the complexity.

Thanks,
Guenter
  
kernel test robot Dec. 17, 2023, 11:49 a.m. UTC | #6
Hi Abdel,

kernel test robot noticed the following build warnings:

[auto build test WARNING on groeck-staging/hwmon-next]
[also build test WARNING on robh/for-next linus/master v6.7-rc5 next-20231215]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]

url:    https://github.com/intel-lab-lkp/linux/commits/Abdel-Alkuor/hwmon-Add-AMS-AS6200-temperature-sensor/20231217-004310
base:   https://git.kernel.org/pub/scm/linux/kernel/git/groeck/linux-staging.git hwmon-next
patch link:    https://lore.kernel.org/r/63e352150ed51eefce90ca4058af5459730174b2.1702744180.git.alkuor%40gmail.com
patch subject: [PATCH 2/2] hwmon: Add AMS AS6200 temperature sensor
reproduce: (https://download.01.org/0day-ci/archive/20231217/202312171951.3y47awo9-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/202312171951.3y47awo9-lkp@intel.com/

All warnings (new ones prefixed by >>):

>> Documentation/hwmon/as6200.rst: WARNING: document isn't included in any toctree
  
kernel test robot Dec. 18, 2023, 12:31 a.m. UTC | #7
Hi Abdel,

kernel test robot noticed the following build warnings:

[auto build test WARNING on groeck-staging/hwmon-next]
[also build test WARNING on robh/for-next linus/master v6.7-rc5 next-20231215]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]

url:    https://github.com/intel-lab-lkp/linux/commits/Abdel-Alkuor/hwmon-Add-AMS-AS6200-temperature-sensor/20231217-004310
base:   https://git.kernel.org/pub/scm/linux/kernel/git/groeck/linux-staging.git hwmon-next
patch link:    https://lore.kernel.org/r/63e352150ed51eefce90ca4058af5459730174b2.1702744180.git.alkuor%40gmail.com
patch subject: [PATCH 2/2] hwmon: Add AMS AS6200 temperature sensor
config: x86_64-randconfig-r132-20231218 (https://download.01.org/0day-ci/archive/20231218/202312180847.bJ22ULTY-lkp@intel.com/config)
compiler: clang version 16.0.4 (https://github.com/llvm/llvm-project.git ae42196bc493ffe877a7e3dff8be32035dea4d07)
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20231218/202312180847.bJ22ULTY-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/202312180847.bJ22ULTY-lkp@intel.com/

sparse warnings: (new ones prefixed by >>)
>> drivers/hwmon/as6200.c:141:24: sparse: sparse: symbol 'as6200_chip_info' was not declared. Should it be static?

vim +/as6200_chip_info +141 drivers/hwmon/as6200.c

   140	
 > 141	struct hwmon_chip_info as6200_chip_info = {
   142		.ops = &as6200_hwmon_ops,
   143		.info = as6200_info
   144	};
   145
  
Abdel Alkuor Dec. 18, 2023, 5:23 a.m. UTC | #8
On Sat, Dec 16, 2023 at 10:06:03PM -0800, Guenter Roeck wrote:
> On 12/16/23 20:59, Abdel Alkuor wrote:
> > On Sat, Dec 16, 2023 at 05:40:35PM -0800, Guenter Roeck wrote:
> > > On 12/16/23 14:07, Abdel Alkuor wrote:
> > > > On Sat, Dec 16, 2023 at 10:46:53AM -0800, Guenter Roeck wrote:
> > > > > On 12/16/23 08:39, Abdel Alkuor wrote:
> >       ...
> >     }
> > 
> > Based on that, the new tmp112 set_mask and clr_mask would look like this instead,
> >    [tmp112] = {
> >    	.set_mask = 3 << 6,	/* 8 samples / second */
> >    	.clr_mask = 1 << 15,	/* no one-shot mode*/
> > 	.config_reg_16bits = 1,
> >    	...
> >    }
> > 
> 
> Yes, you are correct, we'll need something like that. lm75_update_interval()
> tries to solve the problem for tmp112, but that doesn't work with
> set_mask/clear_mask. We should have a separate function lm75_read_config(),
> though, to hide the complexity.
>
I'll fix tmp112 parameters in another patch as as6200 patch 2 in v2
implements 2bytes read/write for the configure reg.
https://marc.info/?l=linux-hwmon&m=170287522119545&w=2

On another note, I checked all the supported chips in lm75 to see
which ones support an alert bit, only 3 chips support it; tmp112(bit 5 in the second byte),
tmp100, and tmp101 (bit 7 in the first byte).

Thanks,
Abdel
  

Patch

diff --git a/Documentation/hwmon/as6200.rst b/Documentation/hwmon/as6200.rst
new file mode 100644
index 000000000000..f6156c2105ff
--- /dev/null
+++ b/Documentation/hwmon/as6200.rst
@@ -0,0 +1,50 @@ 
+Kernel driver as6200
+====================
+
+Supported chips:
+
+  * AMS OSRAM AS6200
+
+    Prefix: 'as6200'
+
+    Addresses scanned: none
+
+    Datasheet: https://ams.com/documents/20143/36005/AS6200_DS000449_4-00.pdf
+
+Author:
+
+	Abdel Alkuor <alkuor@gmail.com>
+
+Description
+-----------
+The AS6200 IC is a temperature sensor with a range between -40°C and 125°C.
+It supports ultra-low power consumption (low operation and quiescent current)
+which makes it ideally suited for mobile/battery powered applications.
+Additionally the AS6200 temperature sensor system also features an alert
+functionality, which triggers an interrupt to protect devices from excessive
+temperatures.
+
+The sensor has an accuracy of ±0.4°C between 0°C and 60°C and has an accuracy
+of 1.0°C from -40°C to +125°C, with Resolution of 0.0625°C. The sensor
+supports conversion rate of 0.25, 1, 4, 8 Hz, and consecutive fault counts of
+1, 2, 4, 6 samples which triggers/clears an alert when high/low temperature
+is detected respectively. Two alert modes are supported, interrupt mode and
+comparator mode.
+
+Configuration Notes
+-------------------
+By default as6200 driver reads the temperature continuously at 8Hz.
+Consecutive faults is set to 6 samples with true polarity which an
+alert is set when the current temperaute goes above high temperature
+theshold and is cleared when it falls below the low temperature threshold.
+Alert is configured in comparator mode.
+
+Interrupt is supported for the alert where user space is notified
+when alert is set/cleared.
+
+sysfs-Interface
+---------------
+temp#_input		 temperature input (read-only)
+temp#_alert              alert flag (read-only)
+temp#_max		 temperature maximum setpoint (read/write)
+temp#_max_hyst		 hysteresis for temperature maximum (read/write)
diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
index cf27523eed5a..f6edcbf1b7cf 100644
--- a/drivers/hwmon/Kconfig
+++ b/drivers/hwmon/Kconfig
@@ -287,6 +287,16 @@  config SENSORS_AS370
 	  This driver can also be built as a module. If so, the module
 	  will be called as370-hwmon.
 
+config SENSORS_AS6200
+	tristate "AMS AS6200 temperature sensor"
+	depends on I2C
+	select REGMAP_I2C
+	help
+	  If you say yes here you get support for AMS AS6200
+	  temperature sensor.
+
+	  This driver can also be built as a module. If so, the module
+	  will be called as6200.
 
 config SENSORS_ASC7621
 	tristate "Andigilog aSC7621"
diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile
index e84bd9685b5c..11fe2b7a80a9 100644
--- a/drivers/hwmon/Makefile
+++ b/drivers/hwmon/Makefile
@@ -53,6 +53,7 @@  obj-$(CONFIG_SENSORS_AQUACOMPUTER_D5NEXT) += aquacomputer_d5next.o
 obj-$(CONFIG_SENSORS_ARM_SCMI)	+= scmi-hwmon.o
 obj-$(CONFIG_SENSORS_ARM_SCPI)	+= scpi-hwmon.o
 obj-$(CONFIG_SENSORS_AS370)	+= as370-hwmon.o
+obj-$(CONFIG_SENSORS_AS6200)	+= as6200.o
 obj-$(CONFIG_SENSORS_ASC7621)	+= asc7621.o
 obj-$(CONFIG_SENSORS_ASPEED)	+= aspeed-pwm-tacho.o
 obj-$(CONFIG_SENSORS_ATXP1)	+= atxp1.o
diff --git a/drivers/hwmon/as6200.c b/drivers/hwmon/as6200.c
new file mode 100644
index 000000000000..173f86e48ee1
--- /dev/null
+++ b/drivers/hwmon/as6200.c
@@ -0,0 +1,249 @@ 
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Driver for AMS AS6200 Temperature sensor
+ *
+ * Author: Abdel Alkuor <alkuor@gmail.com>
+ */
+
+#include <linux/bitfield.h>
+#include <linux/device.h>
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/regmap.h>
+
+#include <linux/hwmon.h>
+
+#define AS6200_TVAL_REG		0x0
+#define AS6200_CONFIG_REG	0x1
+#define AS6200_TLOW_REG		0x2
+#define AS6200_THIGH_REG	0x3
+
+#define AS6200_CONFIG_AL	BIT(5)
+#define AS6200_CONFIG_CR	GENMASK(7, 6)
+#define AS6200_CONFIG_SM	BIT(8)
+#define AS6200_CONFIG_IM	BIT(9)
+#define AS6200_CONFIG_POL	BIT(10)
+#define AS6200_CONFIG_CF	GENMASK(12, 11)
+
+#define AS6200_TEMP_MASK	GENMASK(15, 4)
+#define AS6200_DEFAULT_CONFIG	(AS6200_CONFIG_CR |\
+				 AS6200_CONFIG_CF |\
+				 AS6200_CONFIG_POL)
+
+static const struct regmap_config as6200_regmap_config = {
+	.reg_bits = 8,
+	.val_bits = 16,
+	.max_register = AS6200_THIGH_REG,
+};
+
+static irqreturn_t as6200_event_handler(int irq, void *private)
+{
+	struct device *hwmon_dev = private;
+
+	hwmon_notify_event(hwmon_dev, hwmon_temp, hwmon_temp_alarm, 0);
+	return IRQ_HANDLED;
+}
+
+static int as6200_read(struct device *dev, enum hwmon_sensor_types type,
+		       u32 attr, int channel, long *val)
+{
+	struct regmap *regmap = dev_get_drvdata(dev);
+	unsigned int regval;
+	unsigned int reg;
+	s32 temp;
+	int ret;
+
+	switch (attr) {
+	case hwmon_temp_input:
+		reg = AS6200_TVAL_REG;
+		break;
+	case hwmon_temp_max_hyst:
+		reg = AS6200_TLOW_REG;
+		break;
+	case hwmon_temp_max:
+		reg = AS6200_THIGH_REG;
+		break;
+	case hwmon_temp_alarm:
+		reg = AS6200_CONFIG_REG;
+		break;
+	default:
+		return -EOPNOTSUPP;
+	}
+
+	ret = regmap_read(regmap, reg, &regval);
+	if (ret)
+		return ret;
+
+	if (reg == AS6200_CONFIG_REG) {
+		*val = FIELD_GET(AS6200_CONFIG_AL, regval);
+	} else {
+		temp = sign_extend32(FIELD_GET(AS6200_TEMP_MASK, regval), 11);
+		*val = DIV_ROUND_CLOSEST(temp * 625, 10);
+	}
+
+	return 0;
+}
+
+static int as6200_write(struct device *dev, enum hwmon_sensor_types type,
+			u32 attr, int channel, long val)
+{
+	struct regmap *regmap = dev_get_drvdata(dev);
+	int reg;
+
+	switch (attr) {
+	case hwmon_temp_max_hyst:
+		reg = AS6200_TLOW_REG;
+		break;
+	case hwmon_temp_max:
+		reg = AS6200_THIGH_REG;
+		break;
+	default:
+		return -EOPNOTSUPP;
+	}
+
+	val = clamp_val(val, -40000, 125000) * 16 / 1000;
+	return regmap_write(regmap, reg, FIELD_PREP(AS6200_TEMP_MASK, val));
+}
+
+static umode_t as6200_is_visible(const void *data, enum hwmon_sensor_types type,
+				 u32 attr, int channel)
+{
+	if (type != hwmon_temp)
+		return 0;
+
+	switch (attr) {
+	case hwmon_temp_input:
+	case hwmon_temp_alarm:
+		return 0444;
+	case hwmon_temp_max_hyst:
+	case hwmon_temp_max:
+		return 0644;
+	default:
+		return 0;
+	}
+}
+
+static const struct hwmon_ops as6200_hwmon_ops = {
+	.is_visible = as6200_is_visible,
+	.read = as6200_read,
+	.write = as6200_write,
+};
+
+static const struct hwmon_channel_info * const as6200_info[] = {
+	HWMON_CHANNEL_INFO(chip, HWMON_C_REGISTER_TZ),
+	HWMON_CHANNEL_INFO(temp,
+			   HWMON_T_INPUT | HWMON_T_MAX |
+			   HWMON_T_MAX_HYST | HWMON_T_ALARM),
+	NULL
+};
+
+struct hwmon_chip_info as6200_chip_info = {
+	.ops = &as6200_hwmon_ops,
+	.info = as6200_info
+};
+
+static int as6200_probe(struct i2c_client *client)
+{
+	struct regmap *regmap;
+	struct device *hwmon_dev;
+	struct device *dev = &client->dev;
+	int ret;
+
+	if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C))
+		return -EINVAL;
+
+	regmap = devm_regmap_init_i2c(client, &as6200_regmap_config);
+	if (IS_ERR(regmap))
+		return PTR_ERR(regmap);
+
+	ret = devm_regulator_get_enable(dev, "vdd");
+	if (ret)
+		return dev_err_probe(dev, ret,
+				     "Could not get and enable regulator %d\n",
+				     ret);
+
+	ret = regmap_write(regmap, AS6200_CONFIG_REG, AS6200_DEFAULT_CONFIG);
+	if (ret)
+		return ret;
+
+	hwmon_dev = devm_hwmon_device_register_with_info(dev, "as6200",
+							 regmap,
+							 &as6200_chip_info,
+							 NULL);
+	if (IS_ERR(hwmon_dev))
+		return PTR_ERR(hwmon_dev);
+
+	if (client->irq) {
+		ret = devm_request_threaded_irq(dev,
+						client->irq,
+						NULL,
+						&as6200_event_handler,
+						IRQF_ONESHOT,
+						client->name,
+						hwmon_dev);
+		if (ret)
+			return ret;
+	}
+
+	i2c_set_clientdata(client, regmap);
+
+	return 0;
+}
+
+static int __maybe_unused as6200_suspend(struct device *dev)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	struct regmap *regmap = i2c_get_clientdata(client);
+
+	if (client->irq)
+		disable_irq(client->irq);
+
+	return regmap_update_bits(regmap, AS6200_CONFIG_REG,
+				  AS6200_CONFIG_SM, AS6200_CONFIG_SM);
+}
+
+static int __maybe_unused as6200_resume(struct device *dev)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	struct regmap *regmap = i2c_get_clientdata(client);
+	int ret;
+
+	ret = regmap_update_bits(regmap, AS6200_CONFIG_REG, AS6200_CONFIG_SM, 0);
+	if (ret)
+		return ret;
+
+	if (client->irq)
+		enable_irq(client->irq);
+
+	return 0;
+}
+
+static DEFINE_SIMPLE_DEV_PM_OPS(as6200_pm_ops, as6200_suspend, as6200_resume);
+
+static const struct i2c_device_id as6200_id_table[] = {
+	{ "as6200", 0 },
+	{ }
+};
+MODULE_DEVICE_TABLE(i2c, as6200_id_table);
+
+static const struct of_device_id as6200_of_match[] = {
+	{ .compatible = "ams,as6200" },
+	{ }
+};
+MODULE_DEVICE_TABLE(of, as6200_of_match);
+
+static struct i2c_driver as6200_driver = {
+	.driver = {
+		.name = "as6200",
+		.pm = pm_sleep_ptr(&as6200_pm_ops),
+		.of_match_table = as6200_of_match,
+	},
+	.probe = as6200_probe,
+	.id_table = as6200_id_table,
+};
+module_i2c_driver(as6200_driver);
+
+MODULE_AUTHOR("Abdel Alkuor <alkuor@gmail.com");
+MODULE_DESCRIPTION("AMS AS6200 Temperature Sensor");
+MODULE_LICENSE("GPL");