Commit 4b6358e1 authored by Abdel Alkuor's avatar Abdel Alkuor Committed by Guenter Roeck
Browse files

hwmon: (lm75) Add AMS AS6200 temperature sensor



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: 4 samples
- Alert state: high polarity
- Alert mode: comparator mode

Interrupt is supported for the alert pin.

Signed-off-by: default avatarAbdel Alkuor <alkuor@gmail.com>
Link: https://lore.kernel.org/r/d1686678991bf8ee0d00cb08ca046798f37ca4b3.1703127334.git.alkuor@gmail.com


Signed-off-by: default avatarGuenter Roeck <linux@roeck-us.net>
parent de9c6033
Loading
Loading
Loading
Loading
+10 −0
Original line number Diff line number Diff line
@@ -133,6 +133,16 @@ Supported chips:

               https://www.nxp.com/docs/en/data-sheet/PCT2075.pdf

  * AMS OSRAM AS6200

    Prefix: 'as6200'

    Addresses scanned: none

    Datasheet: Publicly available at the AMS website

               https://ams.com/documents/20143/36005/AS6200_DS000449_4-00.pdf

Author: Frodo Looijaard <frodol@dds.nl>

Description
+94 −14
Original line number Diff line number Diff line
@@ -7,6 +7,7 @@

#include <linux/module.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/slab.h>
#include <linux/jiffies.h>
#include <linux/i2c.h>
@@ -24,6 +25,7 @@

enum lm75_type {		/* keep sorted in alphabetical order */
	adt75,
	as6200,
	at30ts74,
	ds1775,
	ds75,
@@ -54,6 +56,7 @@ enum lm75_type { /* keep sorted in alphabetical order */

/**
 * struct lm75_params - lm75 configuration parameters.
 * @config_reg_16bits:	Configure register size is 2 bytes.
 * @set_mask:		Bits to set in configuration register when configuring
 *			the chip.
 * @clr_mask:		Bits to clear in configuration register when configuring
@@ -74,17 +77,20 @@ enum lm75_type { /* keep sorted in alphabetical order */
 * @sample_times:	All the possible sample times to be set. Mandatory if
 *			num_sample_times is larger than 1. If set, number of
 *			entries must match num_sample_times.
 * @alarm:		Alarm bit is supported.
 */

struct lm75_params {
	u8			set_mask;
	u8			clr_mask;
	bool			config_reg_16bits;
	u16			set_mask;
	u16			clr_mask;
	u8			default_resolution;
	u8			resolution_limits;
	const u8		*resolutions;
	unsigned int		default_sample_time;
	u8			num_sample_times;
	const unsigned int	*sample_times;
	bool			alarm;
};

/* Addresses scanned */
@@ -103,8 +109,8 @@ struct lm75_data {
	struct i2c_client		*client;
	struct regmap			*regmap;
	struct regulator		*vs;
	u8				orig_conf;
	u8				current_conf;
	u16				orig_conf;
	u16				current_conf;
	u8				resolution;	/* In bits, 9 to 16 */
	unsigned int			sample_time;	/* In ms */
	enum lm75_type			kind;
@@ -127,6 +133,15 @@ static const struct lm75_params device_params[] = {
		.default_resolution = 12,
		.default_sample_time = MSEC_PER_SEC / 10,
	},
	[as6200] = {
		.config_reg_16bits = true,
		.set_mask = 0x94C0,	/* 8 sample/s, 4 CF, positive polarity */
		.default_resolution = 12,
		.default_sample_time = 125,
		.num_sample_times = 4,
		.sample_times = (unsigned int []){ 125, 250, 1000, 4000 },
		.alarm = true,
	},
	[at30ts74] = {
		.set_mask = 3 << 5,	/* 12-bit mode*/
		.default_resolution = 12,
@@ -316,19 +331,22 @@ static inline long lm75_reg_to_mc(s16 temp, u8 resolution)
	return ((temp >> (16 - resolution)) * 1000) >> (resolution - 8);
}

static int lm75_write_config(struct lm75_data *data, u8 set_mask,
			     u8 clr_mask)
static int lm75_write_config(struct lm75_data *data, u16 set_mask,
			     u16 clr_mask)
{
	u8 value;
	unsigned int value;

	clr_mask |= LM75_SHUTDOWN;
	clr_mask |= LM75_SHUTDOWN << (8 * data->params->config_reg_16bits);
	value = data->current_conf & ~clr_mask;
	value |= set_mask;

	if (data->current_conf != value) {
		s32 err;

		err = i2c_smbus_write_byte_data(data->client, LM75_REG_CONF,
		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);
		if (err)
			return err;
@@ -337,6 +355,27 @@ static int lm75_write_config(struct lm75_data *data, u8 set_mask,
	return 0;
}

static int lm75_read_config(struct lm75_data *data)
{
	int ret;
	unsigned int status;

	if (data->params->config_reg_16bits) {
		ret = regmap_read(data->regmap, LM75_REG_CONF, &status);
		return ret ? ret : status;
	}

	return i2c_smbus_read_byte_data(data->client, LM75_REG_CONF);
}

static irqreturn_t lm75_alarm_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 lm75_read(struct device *dev, enum hwmon_sensor_types type,
		     u32 attr, int channel, long *val)
{
@@ -365,6 +404,9 @@ static int lm75_read(struct device *dev, enum hwmon_sensor_types type,
		case hwmon_temp_max_hyst:
			reg = LM75_REG_HYST;
			break;
		case hwmon_temp_alarm:
			reg = LM75_REG_CONF;
			break;
		default:
			return -EINVAL;
		}
@@ -372,7 +414,17 @@ static int lm75_read(struct device *dev, enum hwmon_sensor_types type,
		if (err < 0)
			return err;

		if (attr == hwmon_temp_alarm) {
			switch (data->kind) {
			case as6200:
				*val = (regval >> 5) & 0x1;
				break;
			default:
				return -EINVAL;
			}
		} else {
			*val = lm75_reg_to_mc(regval, data->resolution);
		}
		break;
	default:
		return -EINVAL;
@@ -435,6 +487,7 @@ static int lm75_update_interval(struct device *dev, long val)
			data->resolution = data->params->resolutions[index];
		break;
	case tmp112:
	case as6200:
		err = regmap_read(data->regmap, LM75_REG_CONF, &reg);
		if (err < 0)
			return err;
@@ -502,6 +555,10 @@ static umode_t lm75_is_visible(const void *data, enum hwmon_sensor_types type,
		case hwmon_temp_max:
		case hwmon_temp_max_hyst:
			return 0644;
		case hwmon_temp_alarm:
			if (config_data->params->alarm)
				return 0444;
			break;
		}
		break;
	default:
@@ -514,7 +571,8 @@ static const struct hwmon_channel_info * const lm75_info[] = {
	HWMON_CHANNEL_INFO(chip,
			   HWMON_C_REGISTER_TZ | HWMON_C_UPDATE_INTERVAL),
	HWMON_CHANNEL_INFO(temp,
			   HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_MAX_HYST),
			   HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_MAX_HYST |
			   HWMON_T_ALARM),
	NULL
};

@@ -622,7 +680,7 @@ static int lm75_probe(struct i2c_client *client)
		return err;

	/* Cache original configuration */
	status = i2c_smbus_read_byte_data(client, LM75_REG_CONF);
	status = lm75_read_config(data);
	if (status < 0) {
		dev_dbg(dev, "Can't read config? %d\n", status);
		return status;
@@ -645,6 +703,23 @@ static int lm75_probe(struct i2c_client *client)
	if (IS_ERR(hwmon_dev))
		return PTR_ERR(hwmon_dev);

	if (client->irq) {
		if (data->params->alarm) {
			err = devm_request_threaded_irq(dev,
							client->irq,
							NULL,
							&lm75_alarm_handler,
							IRQF_ONESHOT,
							client->name,
							hwmon_dev);
			if (err)
				return err;
		} else {
			 /* alarm is only supported for chips with alarm bit */
			dev_err(dev, "alarm interrupt is not supported\n");
		}
	}

	dev_info(dev, "%s: sensor '%s'\n", dev_name(hwmon_dev), client->name);

	return 0;
@@ -652,6 +727,7 @@ static int lm75_probe(struct i2c_client *client)

static const struct i2c_device_id lm75_ids[] = {
	{ "adt75", adt75, },
	{ "as6200", as6200, },
	{ "at30ts74", at30ts74, },
	{ "ds1775", ds1775, },
	{ "ds75", ds75, },
@@ -688,6 +764,10 @@ static const struct of_device_id __maybe_unused lm75_of_match[] = {
		.compatible = "adi,adt75",
		.data = (void *)adt75
	},
	{
		.compatible = "ams,as6200",
		.data = (void *)as6200
	},
	{
		.compatible = "atmel,at30ts74",
		.data = (void *)at30ts74