diff options
Diffstat (limited to 'drivers/hwmon/adc128d818.c')
| -rw-r--r-- | drivers/hwmon/adc128d818.c | 491 | 
1 files changed, 491 insertions, 0 deletions
diff --git a/drivers/hwmon/adc128d818.c b/drivers/hwmon/adc128d818.c new file mode 100644 index 00000000000..0625e50d7a6 --- /dev/null +++ b/drivers/hwmon/adc128d818.c @@ -0,0 +1,491 @@ +/* + * Driver for TI ADC128D818 System Monitor with Temperature Sensor + * + * Copyright (c) 2014 Guenter Roeck + * + * Derived from lm80.c + * Copyright (C) 1998, 1999  Frodo Looijaard <frodol@dds.nl> + *			     and Philip Edelbrock <phil@netroedge.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + */ + +#include <linux/module.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/regulator/consumer.h> +#include <linux/mutex.h> + +/* Addresses to scan + * The chip also supports addresses 0x35..0x37. Don't scan those addresses + * since they are also used by some EEPROMs, which may result in false + * positives. + */ +static const unsigned short normal_i2c[] = { +	0x1d, 0x1e, 0x1f, 0x2d, 0x2e, 0x2f, I2C_CLIENT_END }; + +/* registers */ +#define ADC128_REG_IN_MAX(nr)		(0x2a + (nr) * 2) +#define ADC128_REG_IN_MIN(nr)		(0x2b + (nr) * 2) +#define ADC128_REG_IN(nr)		(0x20 + (nr)) + +#define ADC128_REG_TEMP			0x27 +#define ADC128_REG_TEMP_MAX		0x38 +#define ADC128_REG_TEMP_HYST		0x39 + +#define ADC128_REG_CONFIG		0x00 +#define ADC128_REG_ALARM		0x01 +#define ADC128_REG_MASK			0x03 +#define ADC128_REG_CONV_RATE		0x07 +#define ADC128_REG_ONESHOT		0x09 +#define ADC128_REG_SHUTDOWN		0x0a +#define ADC128_REG_CONFIG_ADV		0x0b +#define ADC128_REG_BUSY_STATUS		0x0c + +#define ADC128_REG_MAN_ID		0x3e +#define ADC128_REG_DEV_ID		0x3f + +struct adc128_data { +	struct i2c_client *client; +	struct regulator *regulator; +	int vref;		/* Reference voltage in mV */ +	struct mutex update_lock; +	bool valid;		/* true if following fields are valid */ +	unsigned long last_updated;	/* In jiffies */ + +	u16 in[3][7];		/* Register value, normalized to 12 bit +				 * 0: input voltage +				 * 1: min limit +				 * 2: max limit +				 */ +	s16 temp[3];		/* Register value, normalized to 9 bit +				 * 0: sensor 1: limit 2: hyst +				 */ +	u8 alarms;		/* alarm register value */ +}; + +static struct adc128_data *adc128_update_device(struct device *dev) +{ +	struct adc128_data *data = dev_get_drvdata(dev); +	struct i2c_client *client = data->client; +	struct adc128_data *ret = data; +	int i, rv; + +	mutex_lock(&data->update_lock); + +	if (time_after(jiffies, data->last_updated + HZ) || !data->valid) { +		for (i = 0; i < 7; i++) { +			rv = i2c_smbus_read_word_swapped(client, +							 ADC128_REG_IN(i)); +			if (rv < 0) +				goto abort; +			data->in[0][i] = rv >> 4; + +			rv = i2c_smbus_read_byte_data(client, +						      ADC128_REG_IN_MIN(i)); +			if (rv < 0) +				goto abort; +			data->in[1][i] = rv << 4; + +			rv = i2c_smbus_read_byte_data(client, +						      ADC128_REG_IN_MAX(i)); +			if (rv < 0) +				goto abort; +			data->in[2][i] = rv << 4; +		} + +		rv = i2c_smbus_read_word_swapped(client, ADC128_REG_TEMP); +		if (rv < 0) +			goto abort; +		data->temp[0] = rv >> 7; + +		rv = i2c_smbus_read_byte_data(client, ADC128_REG_TEMP_MAX); +		if (rv < 0) +			goto abort; +		data->temp[1] = rv << 1; + +		rv = i2c_smbus_read_byte_data(client, ADC128_REG_TEMP_HYST); +		if (rv < 0) +			goto abort; +		data->temp[2] = rv << 1; + +		rv = i2c_smbus_read_byte_data(client, ADC128_REG_ALARM); +		if (rv < 0) +			goto abort; +		data->alarms |= rv; + +		data->last_updated = jiffies; +		data->valid = true; +	} +	goto done; + +abort: +	ret = ERR_PTR(rv); +	data->valid = false; +done: +	mutex_unlock(&data->update_lock); +	return ret; +} + +static ssize_t adc128_show_in(struct device *dev, struct device_attribute *attr, +			      char *buf) +{ +	struct adc128_data *data = adc128_update_device(dev); +	int index = to_sensor_dev_attr_2(attr)->index; +	int nr = to_sensor_dev_attr_2(attr)->nr; +	int val; + +	if (IS_ERR(data)) +		return PTR_ERR(data); + +	val = DIV_ROUND_CLOSEST(data->in[index][nr] * data->vref, 4095); +	return sprintf(buf, "%d\n", val); +} + +static ssize_t adc128_set_in(struct device *dev, struct device_attribute *attr, +			     const char *buf, size_t count) +{ +	struct adc128_data *data = dev_get_drvdata(dev); +	int index = to_sensor_dev_attr_2(attr)->index; +	int nr = to_sensor_dev_attr_2(attr)->nr; +	u8 reg, regval; +	long val; +	int err; + +	err = kstrtol(buf, 10, &val); +	if (err < 0) +		return err; + +	mutex_lock(&data->update_lock); +	/* 10 mV LSB on limit registers */ +	regval = clamp_val(DIV_ROUND_CLOSEST(val, 10), 0, 255); +	data->in[index][nr] = regval << 4; +	reg = index == 1 ? ADC128_REG_IN_MIN(nr) : ADC128_REG_IN_MAX(nr); +	i2c_smbus_write_byte_data(data->client, reg, regval); +	mutex_unlock(&data->update_lock); + +	return count; +} + +static ssize_t adc128_show_temp(struct device *dev, +				struct device_attribute *attr, char *buf) +{ +	struct adc128_data *data = adc128_update_device(dev); +	int index = to_sensor_dev_attr(attr)->index; +	int temp; + +	if (IS_ERR(data)) +		return PTR_ERR(data); + +	temp = (data->temp[index] << 7) >> 7;	/* sign extend */ +	return sprintf(buf, "%d\n", temp * 500);/* 0.5 degrees C resolution */ +} + +static ssize_t adc128_set_temp(struct device *dev, +			       struct device_attribute *attr, +			       const char *buf, size_t count) +{ +	struct adc128_data *data = dev_get_drvdata(dev); +	int index = to_sensor_dev_attr(attr)->index; +	long val; +	int err; +	s8 regval; + +	err = kstrtol(buf, 10, &val); +	if (err < 0) +		return err; + +	mutex_lock(&data->update_lock); +	regval = clamp_val(DIV_ROUND_CLOSEST(val, 1000), -128, 127); +	data->temp[index] = regval << 1; +	i2c_smbus_write_byte_data(data->client, +				  index == 1 ? ADC128_REG_TEMP_MAX +					     : ADC128_REG_TEMP_HYST, +				  regval); +	mutex_unlock(&data->update_lock); + +	return count; +} + +static ssize_t adc128_show_alarm(struct device *dev, +				 struct device_attribute *attr, char *buf) +{ +	struct adc128_data *data = adc128_update_device(dev); +	int mask = 1 << to_sensor_dev_attr(attr)->index; +	u8 alarms; + +	if (IS_ERR(data)) +		return PTR_ERR(data); + +	/* +	 * Clear an alarm after reporting it to user space. If it is still +	 * active, the next update sequence will set the alarm bit again. +	 */ +	alarms = data->alarms; +	data->alarms &= ~mask; + +	return sprintf(buf, "%u\n", !!(alarms & mask)); +} + +static SENSOR_DEVICE_ATTR_2(in0_input, S_IRUGO, +			    adc128_show_in, NULL, 0, 0); +static SENSOR_DEVICE_ATTR_2(in0_min, S_IWUSR | S_IRUGO, +			    adc128_show_in, adc128_set_in, 0, 1); +static SENSOR_DEVICE_ATTR_2(in0_max, S_IWUSR | S_IRUGO, +			    adc128_show_in, adc128_set_in, 0, 2); + +static SENSOR_DEVICE_ATTR_2(in1_input, S_IRUGO, +			    adc128_show_in, NULL, 1, 0); +static SENSOR_DEVICE_ATTR_2(in1_min, S_IWUSR | S_IRUGO, +			    adc128_show_in, adc128_set_in, 1, 1); +static SENSOR_DEVICE_ATTR_2(in1_max, S_IWUSR | S_IRUGO, +			    adc128_show_in, adc128_set_in, 1, 2); + +static SENSOR_DEVICE_ATTR_2(in2_input, S_IRUGO, +			    adc128_show_in, NULL, 2, 0); +static SENSOR_DEVICE_ATTR_2(in2_min, S_IWUSR | S_IRUGO, +			    adc128_show_in, adc128_set_in, 2, 1); +static SENSOR_DEVICE_ATTR_2(in2_max, S_IWUSR | S_IRUGO, +			    adc128_show_in, adc128_set_in, 2, 2); + +static SENSOR_DEVICE_ATTR_2(in3_input, S_IRUGO, +			    adc128_show_in, NULL, 3, 0); +static SENSOR_DEVICE_ATTR_2(in3_min, S_IWUSR | S_IRUGO, +			    adc128_show_in, adc128_set_in, 3, 1); +static SENSOR_DEVICE_ATTR_2(in3_max, S_IWUSR | S_IRUGO, +			    adc128_show_in, adc128_set_in, 3, 2); + +static SENSOR_DEVICE_ATTR_2(in4_input, S_IRUGO, +			    adc128_show_in, NULL, 4, 0); +static SENSOR_DEVICE_ATTR_2(in4_min, S_IWUSR | S_IRUGO, +			    adc128_show_in, adc128_set_in, 4, 1); +static SENSOR_DEVICE_ATTR_2(in4_max, S_IWUSR | S_IRUGO, +			    adc128_show_in, adc128_set_in, 4, 2); + +static SENSOR_DEVICE_ATTR_2(in5_input, S_IRUGO, +			    adc128_show_in, NULL, 5, 0); +static SENSOR_DEVICE_ATTR_2(in5_min, S_IWUSR | S_IRUGO, +			    adc128_show_in, adc128_set_in, 5, 1); +static SENSOR_DEVICE_ATTR_2(in5_max, S_IWUSR | S_IRUGO, +			    adc128_show_in, adc128_set_in, 5, 2); + +static SENSOR_DEVICE_ATTR_2(in6_input, S_IRUGO, +			    adc128_show_in, NULL, 6, 0); +static SENSOR_DEVICE_ATTR_2(in6_min, S_IWUSR | S_IRUGO, +			    adc128_show_in, adc128_set_in, 6, 1); +static SENSOR_DEVICE_ATTR_2(in6_max, S_IWUSR | S_IRUGO, +			    adc128_show_in, adc128_set_in, 6, 2); + +static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, adc128_show_temp, NULL, 0); +static SENSOR_DEVICE_ATTR(temp1_max, S_IWUSR | S_IRUGO, +			  adc128_show_temp, adc128_set_temp, 1); +static SENSOR_DEVICE_ATTR(temp1_max_hyst, S_IWUSR | S_IRUGO, +			  adc128_show_temp, adc128_set_temp, 2); + +static SENSOR_DEVICE_ATTR(in0_alarm, S_IRUGO, adc128_show_alarm, NULL, 0); +static SENSOR_DEVICE_ATTR(in1_alarm, S_IRUGO, adc128_show_alarm, NULL, 1); +static SENSOR_DEVICE_ATTR(in2_alarm, S_IRUGO, adc128_show_alarm, NULL, 2); +static SENSOR_DEVICE_ATTR(in3_alarm, S_IRUGO, adc128_show_alarm, NULL, 3); +static SENSOR_DEVICE_ATTR(in4_alarm, S_IRUGO, adc128_show_alarm, NULL, 4); +static SENSOR_DEVICE_ATTR(in5_alarm, S_IRUGO, adc128_show_alarm, NULL, 5); +static SENSOR_DEVICE_ATTR(in6_alarm, S_IRUGO, adc128_show_alarm, NULL, 6); +static SENSOR_DEVICE_ATTR(temp1_max_alarm, S_IRUGO, adc128_show_alarm, NULL, 7); + +static struct attribute *adc128_attrs[] = { +	&sensor_dev_attr_in0_min.dev_attr.attr, +	&sensor_dev_attr_in1_min.dev_attr.attr, +	&sensor_dev_attr_in2_min.dev_attr.attr, +	&sensor_dev_attr_in3_min.dev_attr.attr, +	&sensor_dev_attr_in4_min.dev_attr.attr, +	&sensor_dev_attr_in5_min.dev_attr.attr, +	&sensor_dev_attr_in6_min.dev_attr.attr, +	&sensor_dev_attr_in0_max.dev_attr.attr, +	&sensor_dev_attr_in1_max.dev_attr.attr, +	&sensor_dev_attr_in2_max.dev_attr.attr, +	&sensor_dev_attr_in3_max.dev_attr.attr, +	&sensor_dev_attr_in4_max.dev_attr.attr, +	&sensor_dev_attr_in5_max.dev_attr.attr, +	&sensor_dev_attr_in6_max.dev_attr.attr, +	&sensor_dev_attr_in0_input.dev_attr.attr, +	&sensor_dev_attr_in1_input.dev_attr.attr, +	&sensor_dev_attr_in2_input.dev_attr.attr, +	&sensor_dev_attr_in3_input.dev_attr.attr, +	&sensor_dev_attr_in4_input.dev_attr.attr, +	&sensor_dev_attr_in5_input.dev_attr.attr, +	&sensor_dev_attr_in6_input.dev_attr.attr, +	&sensor_dev_attr_temp1_input.dev_attr.attr, +	&sensor_dev_attr_temp1_max.dev_attr.attr, +	&sensor_dev_attr_temp1_max_hyst.dev_attr.attr, +	&sensor_dev_attr_in0_alarm.dev_attr.attr, +	&sensor_dev_attr_in1_alarm.dev_attr.attr, +	&sensor_dev_attr_in2_alarm.dev_attr.attr, +	&sensor_dev_attr_in3_alarm.dev_attr.attr, +	&sensor_dev_attr_in4_alarm.dev_attr.attr, +	&sensor_dev_attr_in5_alarm.dev_attr.attr, +	&sensor_dev_attr_in6_alarm.dev_attr.attr, +	&sensor_dev_attr_temp1_max_alarm.dev_attr.attr, +	NULL +}; +ATTRIBUTE_GROUPS(adc128); + +static int adc128_detect(struct i2c_client *client, struct i2c_board_info *info) +{ +	int man_id, dev_id; + +	if (!i2c_check_functionality(client->adapter, +				     I2C_FUNC_SMBUS_BYTE_DATA | +				     I2C_FUNC_SMBUS_WORD_DATA)) +		return -ENODEV; + +	man_id = i2c_smbus_read_byte_data(client, ADC128_REG_MAN_ID); +	dev_id = i2c_smbus_read_byte_data(client, ADC128_REG_DEV_ID); +	if (man_id != 0x01 || dev_id != 0x09) +		return -ENODEV; + +	/* Check unused bits for confirmation */ +	if (i2c_smbus_read_byte_data(client, ADC128_REG_CONFIG) & 0xf4) +		return -ENODEV; +	if (i2c_smbus_read_byte_data(client, ADC128_REG_CONV_RATE) & 0xfe) +		return -ENODEV; +	if (i2c_smbus_read_byte_data(client, ADC128_REG_ONESHOT) & 0xfe) +		return -ENODEV; +	if (i2c_smbus_read_byte_data(client, ADC128_REG_SHUTDOWN) & 0xfe) +		return -ENODEV; +	if (i2c_smbus_read_byte_data(client, ADC128_REG_CONFIG_ADV) & 0xf8) +		return -ENODEV; +	if (i2c_smbus_read_byte_data(client, ADC128_REG_BUSY_STATUS) & 0xfc) +		return -ENODEV; + +	strlcpy(info->type, "adc128d818", I2C_NAME_SIZE); + +	return 0; +} + +static int adc128_init_client(struct adc128_data *data) +{ +	struct i2c_client *client = data->client; +	int err; + +	/* +	 * Reset chip to defaults. +	 * This makes most other initializations unnecessary. +	 */ +	err = i2c_smbus_write_byte_data(client, ADC128_REG_CONFIG, 0x80); +	if (err) +		return err; + +	/* Start monitoring */ +	err = i2c_smbus_write_byte_data(client, ADC128_REG_CONFIG, 0x01); +	if (err) +		return err; + +	/* If external vref is selected, configure the chip to use it */ +	if (data->regulator) { +		err = i2c_smbus_write_byte_data(client, +						ADC128_REG_CONFIG_ADV, 0x01); +		if (err) +			return err; +	} + +	return 0; +} + +static int adc128_probe(struct i2c_client *client, +			const struct i2c_device_id *id) +{ +	struct device *dev = &client->dev; +	struct regulator *regulator; +	struct device *hwmon_dev; +	struct adc128_data *data; +	int err, vref; + +	data = devm_kzalloc(dev, sizeof(struct adc128_data), GFP_KERNEL); +	if (!data) +		return -ENOMEM; + +	/* vref is optional. If specified, is used as chip reference voltage */ +	regulator = devm_regulator_get_optional(dev, "vref"); +	if (!IS_ERR(regulator)) { +		data->regulator = regulator; +		err = regulator_enable(regulator); +		if (err < 0) +			return err; +		vref = regulator_get_voltage(regulator); +		if (vref < 0) { +			err = vref; +			goto error; +		} +		data->vref = DIV_ROUND_CLOSEST(vref, 1000); +	} else { +		data->vref = 2560;	/* 2.56V, in mV */ +	} + +	data->client = client; +	i2c_set_clientdata(client, data); +	mutex_init(&data->update_lock); + +	/* Initialize the chip */ +	err = adc128_init_client(data); +	if (err < 0) +		goto error; + +	hwmon_dev = devm_hwmon_device_register_with_groups(dev, client->name, +							   data, adc128_groups); +	if (IS_ERR(hwmon_dev)) { +		err = PTR_ERR(hwmon_dev); +		goto error; +	} + +	return 0; + +error: +	if (data->regulator) +		regulator_disable(data->regulator); +	return err; +} + +static int adc128_remove(struct i2c_client *client) +{ +	struct adc128_data *data = i2c_get_clientdata(client); + +	if (data->regulator) +		regulator_disable(data->regulator); + +	return 0; +} + +static const struct i2c_device_id adc128_id[] = { +	{ "adc128d818", 0 }, +	{ } +}; +MODULE_DEVICE_TABLE(i2c, adc128_id); + +static struct i2c_driver adc128_driver = { +	.class		= I2C_CLASS_HWMON, +	.driver = { +		.name	= "adc128d818", +	}, +	.probe		= adc128_probe, +	.remove		= adc128_remove, +	.id_table	= adc128_id, +	.detect		= adc128_detect, +	.address_list	= normal_i2c, +}; + +module_i2c_driver(adc128_driver); + +MODULE_AUTHOR("Guenter Roeck"); +MODULE_DESCRIPTION("Driver for ADC128D818"); +MODULE_LICENSE("GPL");  | 
