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: Abdel Alkuor <alkuor@gmail.com>
Link: https://lore.kernel.org/r/d1686678991bf8ee0d00cb08ca046798f37ca4b3.1703127334.git.alkuor@gmail.com
Signed-off-by: Guenter Roeck <linux@roeck-us.net>
This commit is contained in:
Abdel Alkuor 2023-12-23 11:21:59 -05:00 committed by Guenter Roeck
parent de9c6033fb
commit 4b6358e1fe
2 changed files with 104 additions and 14 deletions

View File

@ -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

View File

@ -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,20 +331,23 @@ 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,
value);
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;
data->current_conf = value;
@ -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;
*val = lm75_reg_to_mc(regval, data->resolution);
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