diff options
Diffstat (limited to 'drivers/hwmon/adm9240.c')
| -rw-r--r-- | drivers/hwmon/adm9240.c | 982 | 
1 files changed, 462 insertions, 520 deletions
| diff --git a/drivers/hwmon/adm9240.c b/drivers/hwmon/adm9240.c index cc3e0184e720..5677263bcf0d 100644 --- a/drivers/hwmon/adm9240.c +++ b/drivers/hwmon/adm9240.c @@ -28,6 +28,7 @@   * LM81 extended temp reading not implemented   */ +#include <linux/bits.h>  #include <linux/init.h>  #include <linux/module.h>  #include <linux/slab.h> @@ -37,7 +38,6 @@  #include <linux/hwmon-vid.h>  #include <linux/err.h>  #include <linux/mutex.h> -#include <linux/jiffies.h>  #include <linux/regmap.h>  /* Addresses to scan */ @@ -123,32 +123,18 @@ static inline unsigned int AOUT_FROM_REG(u8 reg)  /* per client data */  struct adm9240_data { -	struct i2c_client *client; +	struct device *dev;  	struct regmap *regmap;  	struct mutex update_lock; -	char valid; -	unsigned long last_updated_measure; -	unsigned long last_updated_config; - -	u8 in[6];		/* ro	in0_input */ -	u8 in_max[6];		/* rw	in0_max */ -	u8 in_min[6];		/* rw	in0_min */ -	u8 fan[2];		/* ro	fan1_input */ -	u8 fan_min[2];		/* rw	fan1_min */ +  	u8 fan_div[2];		/* rw	fan1_div, read-only accessor */ -	s16 temp;		/* ro	temp1_input, 9-bit sign-extended */ -	s8 temp_max[2];		/* rw	0 -> temp_max, 1 -> temp_max_hyst */ -	u16 alarms;		/* ro	alarms */ -	u8 aout;		/* rw	aout_output */ -	u8 vid;			/* ro	vid */  	u8 vrm;			/* --	vrm set on startup, no accessor */  };  /* write new fan div, callers must hold data->update_lock */ -static int adm9240_write_fan_div(struct adm9240_data *data, int nr, -		u8 fan_div) +static int adm9240_write_fan_div(struct adm9240_data *data, int channel, u8 fan_div)  { -	unsigned int reg, old, shift = (nr + 2) * 2; +	unsigned int reg, old, shift = (channel + 2) * 2;  	int err;  	err = regmap_read(data->regmap, ADM9240_REG_VID_FAN_DIV, ®); @@ -160,336 +146,13 @@ static int adm9240_write_fan_div(struct adm9240_data *data, int nr,  	err = regmap_write(data->regmap, ADM9240_REG_VID_FAN_DIV, reg);  	if (err < 0)  		return err; -	dev_dbg(&data->client->dev, -		"fan%d clock divider changed from %u to %u\n", -		nr + 1, 1 << old, 1 << fan_div); - -	return 0; -} - -static int adm9240_update_measure(struct adm9240_data *data) -{ -	unsigned int val; -	u8 regs[2]; -	int err; -	int i; - -	err = regmap_bulk_read(data->regmap, ADM9240_REG_IN(0), &data->in[0], 6); -	if (err < 0) -		return err; -	err = regmap_bulk_read(data->regmap, ADM9240_REG_INT(0), ®s, 2); -	if (err < 0) -		return err; - -	data->alarms = regs[0] | regs[1] << 8; - -	/* -	 * read temperature: assume temperature changes less than -	 * 0.5'C per two measurement cycles thus ignore possible -	 * but unlikely aliasing error on lsb reading. --Grant -	 */ -	err = regmap_read(data->regmap, ADM9240_REG_TEMP, &val); -	if (err < 0) -		return err; -	data->temp = val << 8; -	err = regmap_read(data->regmap, ADM9240_REG_TEMP_CONF, &val); -	if (err < 0) -		return err; -	data->temp |= val; - -	err = regmap_bulk_read(data->regmap, ADM9240_REG_FAN(0), -			       &data->fan[0], 2); -	if (err < 0) -		return err; - -	for (i = 0; i < 2; i++) { /* read fans */ -		/* adjust fan clock divider on overflow */ -		if (data->valid && data->fan[i] == 255 && -				data->fan_div[i] < 3) { - -			err = adm9240_write_fan_div(data, i, -					++data->fan_div[i]); -			if (err < 0) -				return err; - -			/* adjust fan_min if active, but not to 0 */ -			if (data->fan_min[i] < 255 && -					data->fan_min[i] >= 2) -				data->fan_min[i] /= 2; -		} -	} +	dev_dbg(data->dev, +		"fan%d clock divider changed from %lu to %lu\n", +		channel + 1, BIT(old), BIT(fan_div));  	return 0;  } -static int adm9240_update_config(struct adm9240_data *data) -{ -	unsigned int val; -	int i; -	int err; - -	for (i = 0; i < 6; i++) { -		err = regmap_raw_read(data->regmap, ADM9240_REG_IN_MIN(i), -				      &data->in_min[i], 1); -		if (err < 0) -			return err; -		err = regmap_raw_read(data->regmap, ADM9240_REG_IN_MAX(i), -				      &data->in_max[i], 1); -		if (err < 0) -			return err; -	} -	err = regmap_bulk_read(data->regmap, ADM9240_REG_FAN_MIN(0), -				      &data->fan_min[0], 2); -	if (err < 0) -		return err; -	err = regmap_bulk_read(data->regmap, ADM9240_REG_TEMP_MAX(0), -				      &data->temp_max[0], 2); -	if (err < 0) -		return err; - -	/* read fan divs and 5-bit VID */ -	err = regmap_read(data->regmap, ADM9240_REG_VID_FAN_DIV, &val); -	if (err < 0) -		return err; -	data->fan_div[0] = (val >> 4) & 3; -	data->fan_div[1] = (val >> 6) & 3; -	data->vid = val & 0x0f; -	err = regmap_read(data->regmap, ADM9240_REG_VID4, &val); -	if (err < 0) -		return err; -	data->vid |= (val & 1) << 4; -	/* read analog out */ -	err = regmap_raw_read(data->regmap, ADM9240_REG_ANALOG_OUT, -			      &data->aout, 1); - -	return err; -} - -static struct adm9240_data *adm9240_update_device(struct device *dev) -{ -	struct adm9240_data *data = dev_get_drvdata(dev); -	int err; - -	mutex_lock(&data->update_lock); - -	/* minimum measurement cycle: 1.75 seconds */ -	if (time_after(jiffies, data->last_updated_measure + (HZ * 7 / 4)) -			|| !data->valid) { -		err = adm9240_update_measure(data); -		if (err < 0) { -			data->valid = 0; -			mutex_unlock(&data->update_lock); -			return ERR_PTR(err); -		} -		data->last_updated_measure = jiffies; -	} - -	/* minimum config reading cycle: 300 seconds */ -	if (time_after(jiffies, data->last_updated_config + (HZ * 300)) -			|| !data->valid) { -		err = adm9240_update_config(data); -		if (err < 0) { -			data->valid = 0; -			mutex_unlock(&data->update_lock); -			return ERR_PTR(err); -		} -		data->last_updated_config = jiffies; -		data->valid = 1; -	} -	mutex_unlock(&data->update_lock); -	return data; -} - -/*** sysfs accessors ***/ - -/* temperature */ -static ssize_t temp1_input_show(struct device *dev, -				struct device_attribute *dummy, char *buf) -{ -	struct adm9240_data *data = adm9240_update_device(dev); - -	if (IS_ERR(data)) -		return PTR_ERR(data); - -	return sprintf(buf, "%d\n", data->temp / 128 * 500); /* 9-bit value */ -} - -static ssize_t max_show(struct device *dev, struct device_attribute *devattr, -			char *buf) -{ -	struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); -	struct adm9240_data *data = adm9240_update_device(dev); - -	if (IS_ERR(data)) -		return PTR_ERR(data); - -	return sprintf(buf, "%d\n", data->temp_max[attr->index] * 1000); -} - -static ssize_t max_store(struct device *dev, struct device_attribute *devattr, -			 const char *buf, size_t count) -{ -	struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); -	struct adm9240_data *data = dev_get_drvdata(dev); -	long val; -	int err; - -	err = kstrtol(buf, 10, &val); -	if (err) -		return err; - -	mutex_lock(&data->update_lock); -	data->temp_max[attr->index] = TEMP_TO_REG(val); -	err = regmap_write(data->regmap, ADM9240_REG_TEMP_MAX(attr->index), -			   data->temp_max[attr->index]); -	mutex_unlock(&data->update_lock); -	return err < 0 ? err : count; -} - -static DEVICE_ATTR_RO(temp1_input); -static SENSOR_DEVICE_ATTR_RW(temp1_max, max, 0); -static SENSOR_DEVICE_ATTR_RW(temp1_max_hyst, max, 1); - -/* voltage */ -static ssize_t in_show(struct device *dev, struct device_attribute *devattr, -		       char *buf) -{ -	struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); -	struct adm9240_data *data = adm9240_update_device(dev); - -	if (IS_ERR(data)) -		return PTR_ERR(data); - -	return sprintf(buf, "%d\n", IN_FROM_REG(data->in[attr->index], -				attr->index)); -} - -static ssize_t in_min_show(struct device *dev, -			   struct device_attribute *devattr, char *buf) -{ -	struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); -	struct adm9240_data *data = adm9240_update_device(dev); - -	if (IS_ERR(data)) -		return PTR_ERR(data); - -	return sprintf(buf, "%d\n", IN_FROM_REG(data->in_min[attr->index], -				attr->index)); -} - -static ssize_t in_max_show(struct device *dev, -			   struct device_attribute *devattr, char *buf) -{ -	struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); -	struct adm9240_data *data = adm9240_update_device(dev); - -	if (IS_ERR(data)) -		return PTR_ERR(data); - -	return sprintf(buf, "%d\n", IN_FROM_REG(data->in_max[attr->index], -				attr->index)); -} - -static ssize_t in_min_store(struct device *dev, -			    struct device_attribute *devattr, const char *buf, -			    size_t count) -{ -	struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); -	struct adm9240_data *data = dev_get_drvdata(dev); -	unsigned long val; -	int err; - -	err = kstrtoul(buf, 10, &val); -	if (err) -		return err; - -	mutex_lock(&data->update_lock); -	data->in_min[attr->index] = IN_TO_REG(val, attr->index); -	err = regmap_write(data->regmap, ADM9240_REG_IN_MIN(attr->index), -			   data->in_min[attr->index]); -	mutex_unlock(&data->update_lock); -	return err < 0 ? err : count; -} - -static ssize_t in_max_store(struct device *dev, -			    struct device_attribute *devattr, const char *buf, -			    size_t count) -{ -	struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); -	struct adm9240_data *data = dev_get_drvdata(dev); -	unsigned long val; -	int err; - -	err = kstrtoul(buf, 10, &val); -	if (err) -		return err; - -	mutex_lock(&data->update_lock); -	data->in_max[attr->index] = IN_TO_REG(val, attr->index); -	err = regmap_write(data->regmap, ADM9240_REG_IN_MAX(attr->index), -			   data->in_max[attr->index]); -	mutex_unlock(&data->update_lock); -	return err < 0 ? err : count; -} - -static SENSOR_DEVICE_ATTR_RO(in0_input, in, 0); -static SENSOR_DEVICE_ATTR_RW(in0_min, in_min, 0); -static SENSOR_DEVICE_ATTR_RW(in0_max, in_max, 0); -static SENSOR_DEVICE_ATTR_RO(in1_input, in, 1); -static SENSOR_DEVICE_ATTR_RW(in1_min, in_min, 1); -static SENSOR_DEVICE_ATTR_RW(in1_max, in_max, 1); -static SENSOR_DEVICE_ATTR_RO(in2_input, in, 2); -static SENSOR_DEVICE_ATTR_RW(in2_min, in_min, 2); -static SENSOR_DEVICE_ATTR_RW(in2_max, in_max, 2); -static SENSOR_DEVICE_ATTR_RO(in3_input, in, 3); -static SENSOR_DEVICE_ATTR_RW(in3_min, in_min, 3); -static SENSOR_DEVICE_ATTR_RW(in3_max, in_max, 3); -static SENSOR_DEVICE_ATTR_RO(in4_input, in, 4); -static SENSOR_DEVICE_ATTR_RW(in4_min, in_min, 4); -static SENSOR_DEVICE_ATTR_RW(in4_max, in_max, 4); -static SENSOR_DEVICE_ATTR_RO(in5_input, in, 5); -static SENSOR_DEVICE_ATTR_RW(in5_min, in_min, 5); -static SENSOR_DEVICE_ATTR_RW(in5_max, in_max, 5); - -/* fans */ -static ssize_t fan_show(struct device *dev, struct device_attribute *devattr, -			char *buf) -{ -	struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); -	struct adm9240_data *data = adm9240_update_device(dev); - -	if (IS_ERR(data)) -		return PTR_ERR(data); - -	return sprintf(buf, "%d\n", FAN_FROM_REG(data->fan[attr->index], -				1 << data->fan_div[attr->index])); -} - -static ssize_t fan_min_show(struct device *dev, -			    struct device_attribute *devattr, char *buf) -{ -	struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); -	struct adm9240_data *data = adm9240_update_device(dev); - -	if (IS_ERR(data)) -		return PTR_ERR(data); - -	return sprintf(buf, "%d\n", FAN_FROM_REG(data->fan_min[attr->index], -				1 << data->fan_div[attr->index])); -} - -static ssize_t fan_div_show(struct device *dev, -			    struct device_attribute *devattr, char *buf) -{ -	struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); -	struct adm9240_data *data = adm9240_update_device(dev); - -	if (IS_ERR(data)) -		return PTR_ERR(data); - -	return sprintf(buf, "%d\n", 1 << data->fan_div[attr->index]); -} -  /*   * set fan speed low limit:   * @@ -501,38 +164,25 @@ static ssize_t fan_div_show(struct device *dev,   * - otherwise: select fan clock divider to suit fan speed low limit,   *   measurement code may adjust registers to ensure fan speed reading   */ -static ssize_t fan_min_store(struct device *dev, -			     struct device_attribute *devattr, -			     const char *buf, size_t count) +static int adm9240_fan_min_write(struct adm9240_data *data, int channel, long val)  { -	struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); -	struct adm9240_data *data = dev_get_drvdata(dev); -	struct i2c_client *client = data->client; -	int nr = attr->index;  	u8 new_div; -	unsigned long val; +	u8 fan_min;  	int err; -	err = kstrtoul(buf, 10, &val); -	if (err) -		return err; -  	mutex_lock(&data->update_lock);  	if (!val) { -		data->fan_min[nr] = 255; -		new_div = data->fan_div[nr]; - -		dev_dbg(&client->dev, "fan%u low limit set disabled\n", -				nr + 1); +		fan_min = 255; +		new_div = data->fan_div[channel]; +		dev_dbg(data->dev, "fan%u low limit set disabled\n", channel + 1);  	} else if (val < 1350000 / (8 * 254)) {  		new_div = 3; -		data->fan_min[nr] = 254; - -		dev_dbg(&client->dev, "fan%u low limit set minimum %u\n", -				nr + 1, FAN_FROM_REG(254, 1 << new_div)); +		fan_min = 254; +		dev_dbg(data->dev, "fan%u low limit set minimum %u\n", +			channel + 1, FAN_FROM_REG(254, BIT(new_div)));  	} else {  		unsigned int new_min = 1350000 / val; @@ -544,87 +194,55 @@ static ssize_t fan_min_store(struct device *dev,  		if (!new_min) /* keep > 0 */  			new_min++; -		data->fan_min[nr] = new_min; +		fan_min = new_min; -		dev_dbg(&client->dev, "fan%u low limit set fan speed %u\n", -				nr + 1, FAN_FROM_REG(new_min, 1 << new_div)); +		dev_dbg(data->dev, "fan%u low limit set fan speed %u\n", +			channel + 1, FAN_FROM_REG(new_min, BIT(new_div)));  	} -	if (new_div != data->fan_div[nr]) { -		data->fan_div[nr] = new_div; -		adm9240_write_fan_div(data, nr, new_div); +	if (new_div != data->fan_div[channel]) { +		data->fan_div[channel] = new_div; +		adm9240_write_fan_div(data, channel, new_div);  	} -	err = regmap_write(data->regmap, ADM9240_REG_FAN_MIN(nr), -			   data->fan_min[nr]); +	err = regmap_write(data->regmap, ADM9240_REG_FAN_MIN(channel), fan_min);  	mutex_unlock(&data->update_lock); -	return err < 0 ? err : count; -} - -static SENSOR_DEVICE_ATTR_RO(fan1_input, fan, 0); -static SENSOR_DEVICE_ATTR_RW(fan1_min, fan_min, 0); -static SENSOR_DEVICE_ATTR_RO(fan1_div, fan_div, 0); -static SENSOR_DEVICE_ATTR_RO(fan2_input, fan, 1); -static SENSOR_DEVICE_ATTR_RW(fan2_min, fan_min, 1); -static SENSOR_DEVICE_ATTR_RO(fan2_div, fan_div, 1); -/* alarms */ -static ssize_t alarms_show(struct device *dev, -		struct device_attribute *attr, char *buf) -{ -	struct adm9240_data *data = adm9240_update_device(dev); - -	if (IS_ERR(data)) -		return PTR_ERR(data); - -	return sprintf(buf, "%u\n", data->alarms); +	return err;  } -static DEVICE_ATTR_RO(alarms); -static ssize_t alarm_show(struct device *dev, struct device_attribute *attr, -			  char *buf) -{ -	int bitnr = to_sensor_dev_attr(attr)->index; -	struct adm9240_data *data = adm9240_update_device(dev); - -	if (IS_ERR(data)) -		return PTR_ERR(data); - -	return sprintf(buf, "%u\n", (data->alarms >> bitnr) & 1); -} -static SENSOR_DEVICE_ATTR_RO(in0_alarm, alarm, 0); -static SENSOR_DEVICE_ATTR_RO(in1_alarm, alarm, 1); -static SENSOR_DEVICE_ATTR_RO(in2_alarm, alarm, 2); -static SENSOR_DEVICE_ATTR_RO(in3_alarm, alarm, 3); -static SENSOR_DEVICE_ATTR_RO(in4_alarm, alarm, 8); -static SENSOR_DEVICE_ATTR_RO(in5_alarm, alarm, 9); -static SENSOR_DEVICE_ATTR_RO(temp1_alarm, alarm, 4); -static SENSOR_DEVICE_ATTR_RO(fan1_alarm, alarm, 6); -static SENSOR_DEVICE_ATTR_RO(fan2_alarm, alarm, 7); - -/* vid */  static ssize_t cpu0_vid_show(struct device *dev,  			     struct device_attribute *attr, char *buf)  { -	struct adm9240_data *data = adm9240_update_device(dev); - -	if (IS_ERR(data)) -		return PTR_ERR(data); +	struct adm9240_data *data = dev_get_drvdata(dev); +	unsigned int regval; +	int err; +	u8 vid; -	return sprintf(buf, "%d\n", vid_from_reg(data->vid, data->vrm)); +	err = regmap_read(data->regmap, ADM9240_REG_VID_FAN_DIV, ®val); +	if (err < 0) +		return err; +	vid = regval & 0x0f; +	err = regmap_read(data->regmap, ADM9240_REG_VID4, ®val); +	if (err < 0) +		return err; +	vid |= (regval & 1) << 4; +	return sprintf(buf, "%d\n", vid_from_reg(vid, data->vrm));  }  static DEVICE_ATTR_RO(cpu0_vid); -/* analog output */  static ssize_t aout_output_show(struct device *dev,  				struct device_attribute *attr, char *buf)  { -	struct adm9240_data *data = adm9240_update_device(dev); +	struct adm9240_data *data = dev_get_drvdata(dev); +	unsigned int regval; +	int err; -	if (IS_ERR(data)) -		return PTR_ERR(data); +	err = regmap_read(data->regmap, ADM9240_REG_ANALOG_OUT, ®val); +	if (err) +		return err; -	return sprintf(buf, "%d\n", AOUT_FROM_REG(data->aout)); +	return sprintf(buf, "%d\n", AOUT_FROM_REG(regval));  }  static ssize_t aout_output_store(struct device *dev, @@ -639,76 +257,13 @@ static ssize_t aout_output_store(struct device *dev,  	if (err)  		return err; -	mutex_lock(&data->update_lock); -	data->aout = AOUT_TO_REG(val); -	err = regmap_write(data->regmap, ADM9240_REG_ANALOG_OUT, data->aout); -	mutex_unlock(&data->update_lock); +	err = regmap_write(data->regmap, ADM9240_REG_ANALOG_OUT, AOUT_TO_REG(val));  	return err < 0 ? err : count;  }  static DEVICE_ATTR_RW(aout_output); -static ssize_t alarm_store(struct device *dev, struct device_attribute *attr, -			   const char *buf, size_t count) -{ -	struct adm9240_data *data = dev_get_drvdata(dev); -	unsigned long val; -	int err; - -	if (kstrtoul(buf, 10, &val) || val != 0) -		return -EINVAL; - -	mutex_lock(&data->update_lock); -	err = regmap_write(data->regmap, ADM9240_REG_CHASSIS_CLEAR, 0x80); -	data->valid = 0;		/* Force cache refresh */ -	mutex_unlock(&data->update_lock); -	if (err < 0) -		return err; -	dev_dbg(&data->client->dev, "chassis intrusion latch cleared\n"); - -	return count; -} -static SENSOR_DEVICE_ATTR_RW(intrusion0_alarm, alarm, 12); -  static struct attribute *adm9240_attrs[] = { -	&sensor_dev_attr_in0_input.dev_attr.attr, -	&sensor_dev_attr_in0_min.dev_attr.attr, -	&sensor_dev_attr_in0_max.dev_attr.attr, -	&sensor_dev_attr_in0_alarm.dev_attr.attr, -	&sensor_dev_attr_in1_input.dev_attr.attr, -	&sensor_dev_attr_in1_min.dev_attr.attr, -	&sensor_dev_attr_in1_max.dev_attr.attr, -	&sensor_dev_attr_in1_alarm.dev_attr.attr, -	&sensor_dev_attr_in2_input.dev_attr.attr, -	&sensor_dev_attr_in2_min.dev_attr.attr, -	&sensor_dev_attr_in2_max.dev_attr.attr, -	&sensor_dev_attr_in2_alarm.dev_attr.attr, -	&sensor_dev_attr_in3_input.dev_attr.attr, -	&sensor_dev_attr_in3_min.dev_attr.attr, -	&sensor_dev_attr_in3_max.dev_attr.attr, -	&sensor_dev_attr_in3_alarm.dev_attr.attr, -	&sensor_dev_attr_in4_input.dev_attr.attr, -	&sensor_dev_attr_in4_min.dev_attr.attr, -	&sensor_dev_attr_in4_max.dev_attr.attr, -	&sensor_dev_attr_in4_alarm.dev_attr.attr, -	&sensor_dev_attr_in5_input.dev_attr.attr, -	&sensor_dev_attr_in5_min.dev_attr.attr, -	&sensor_dev_attr_in5_max.dev_attr.attr, -	&sensor_dev_attr_in5_alarm.dev_attr.attr, -	&dev_attr_temp1_input.attr, -	&sensor_dev_attr_temp1_max.dev_attr.attr, -	&sensor_dev_attr_temp1_max_hyst.dev_attr.attr, -	&sensor_dev_attr_temp1_alarm.dev_attr.attr, -	&sensor_dev_attr_fan1_input.dev_attr.attr, -	&sensor_dev_attr_fan1_div.dev_attr.attr, -	&sensor_dev_attr_fan1_min.dev_attr.attr, -	&sensor_dev_attr_fan1_alarm.dev_attr.attr, -	&sensor_dev_attr_fan2_input.dev_attr.attr, -	&sensor_dev_attr_fan2_div.dev_attr.attr, -	&sensor_dev_attr_fan2_min.dev_attr.attr, -	&sensor_dev_attr_fan2_alarm.dev_attr.attr, -	&dev_attr_alarms.attr,  	&dev_attr_aout_output.attr, -	&sensor_dev_attr_intrusion0_alarm.dev_attr.attr,  	&dev_attr_cpu0_vid.attr,  	NULL  }; @@ -730,26 +285,19 @@ static int adm9240_detect(struct i2c_client *new_client,  		return -ENODEV;  	/* verify chip: reg address should match i2c address */ -	if (i2c_smbus_read_byte_data(new_client, ADM9240_REG_I2C_ADDR) -			!= address) { -		dev_err(&adapter->dev, "detect fail: address match, 0x%02x\n", -			address); +	if (i2c_smbus_read_byte_data(new_client, ADM9240_REG_I2C_ADDR) != address)  		return -ENODEV; -	}  	/* check known chip manufacturer */  	man_id = i2c_smbus_read_byte_data(new_client, ADM9240_REG_MAN_ID); -	if (man_id == 0x23) { +	if (man_id == 0x23)  		name = "adm9240"; -	} else if (man_id == 0xda) { +	else if (man_id == 0xda)  		name = "ds1780"; -	} else if (man_id == 0x01) { +	else if (man_id == 0x01)  		name = "lm81"; -	} else { -		dev_err(&adapter->dev, "detect fail: unknown manuf, 0x%02x\n", -			man_id); +	else  		return -ENODEV; -	}  	/* successful detect, print chip info */  	die_rev = i2c_smbus_read_byte_data(new_client, ADM9240_REG_DIE_REV); @@ -757,13 +305,14 @@ static int adm9240_detect(struct i2c_client *new_client,  		 man_id == 0x23 ? "ADM9240" :  		 man_id == 0xda ? "DS1780" : "LM81", die_rev); -	strlcpy(info->type, name, I2C_NAME_SIZE); +	strscpy(info->type, name, I2C_NAME_SIZE);  	return 0;  } -static int adm9240_init_client(struct i2c_client *client, struct adm9240_data *data) +static int adm9240_init_client(struct adm9240_data *data)  { +	unsigned int regval;  	u8 conf, mode;  	int err; @@ -777,13 +326,13 @@ static int adm9240_init_client(struct i2c_client *client, struct adm9240_data *d  	data->vrm = vid_which_vrm(); /* need this to report vid as mV */ -	dev_info(&client->dev, "Using VRM: %d.%d\n", data->vrm / 10, -			data->vrm % 10); +	dev_info(data->dev, "Using VRM: %d.%d\n", data->vrm / 10, +		 data->vrm % 10);  	if (conf & 1) { /* measurement cycle running: report state */ -		dev_info(&client->dev, "status: config 0x%02x mode %u\n", -				conf, mode); +		dev_info(data->dev, "status: config 0x%02x mode %u\n", +			 conf, mode);  	} else { /* cold start: open limits before starting chip */  		int i; @@ -800,13 +349,13 @@ static int adm9240_init_client(struct i2c_client *client, struct adm9240_data *d  		}  		for (i = 0; i < 2; i++) {  			err = regmap_write(data->regmap, -					ADM9240_REG_FAN_MIN(i), 255); +					   ADM9240_REG_FAN_MIN(i), 255);  			if (err < 0)  				return err;  		}  		for (i = 0; i < 2; i++) {  			err = regmap_write(data->regmap, -					ADM9240_REG_TEMP_MAX(i), 127); +					   ADM9240_REG_TEMP_MAX(i), 127);  			if (err < 0)  				return err;  		} @@ -816,23 +365,417 @@ static int adm9240_init_client(struct i2c_client *client, struct adm9240_data *d  		if (err < 0)  			return err; -		dev_info(&client->dev, +		dev_info(data->dev,  			 "cold start: config was 0x%02x mode %u\n", conf, mode);  	} +	/* read fan divs */ +	err = regmap_read(data->regmap, ADM9240_REG_VID_FAN_DIV, ®val); +	if (err < 0) +		return err; +	data->fan_div[0] = (regval >> 4) & 3; +	data->fan_div[1] = (regval >> 6) & 3;  	return 0;  } +static int adm9240_chip_read(struct device *dev, u32 attr, long *val) +{ +	struct adm9240_data *data = dev_get_drvdata(dev); +	u8 regs[2]; +	int err; + +	switch (attr) { +	case hwmon_chip_alarms: +		err = regmap_bulk_read(data->regmap, ADM9240_REG_INT(0), ®s, 2); +		if (err < 0) +			return err; +		*val = regs[0] | regs[1] << 8; +		break; +	default: +		return -EOPNOTSUPP; +	} +	return 0; +} + +static int adm9240_intrusion_read(struct device *dev, u32 attr, long *val) +{ +	struct adm9240_data *data = dev_get_drvdata(dev); +	unsigned int regval; +	int err; + +	switch (attr) { +	case hwmon_intrusion_alarm: +		err = regmap_read(data->regmap, ADM9240_REG_INT(1), ®val); +		if (err < 0) +			return err; +		*val = !!(regval & BIT(4)); +		break; +	default: +		return -EOPNOTSUPP; +	} +	return 0; +} + +static int adm9240_intrusion_write(struct device *dev, u32 attr, long val) +{ +	struct adm9240_data *data = dev_get_drvdata(dev); +	int err; + +	switch (attr) { +	case hwmon_intrusion_alarm: +		if (val) +			return -EINVAL; +		err = regmap_write(data->regmap, ADM9240_REG_CHASSIS_CLEAR, 0x80); +		if (err < 0) +			return err; +		dev_dbg(data->dev, "chassis intrusion latch cleared\n"); +		break; +	default: +		return -EOPNOTSUPP; +	} +	return 0; +} + +static int adm9240_in_read(struct device *dev, u32 attr, int channel, long *val) +{ +	struct adm9240_data *data = dev_get_drvdata(dev); +	unsigned int regval; +	int reg; +	int err; + +	switch (attr) { +	case hwmon_in_input: +		reg = ADM9240_REG_IN(channel); +		break; +	case hwmon_in_min: +		reg = ADM9240_REG_IN_MIN(channel); +		break; +	case hwmon_in_max: +		reg = ADM9240_REG_IN_MAX(channel); +		break; +	case hwmon_in_alarm: +		if (channel < 4) { +			reg = ADM9240_REG_INT(0); +		} else { +			reg = ADM9240_REG_INT(1); +			channel -= 4; +		} +		err = regmap_read(data->regmap, reg, ®val); +		if (err < 0) +			return err; +		*val = !!(regval & BIT(channel)); +		return 0; +	default: +		return -EOPNOTSUPP; +	} +	err = regmap_read(data->regmap, reg, ®val); +	if (err < 0) +		return err; +	*val = IN_FROM_REG(regval, channel); +	return 0; +} + +static int adm9240_in_write(struct device *dev, u32 attr, int channel, long val) +{ +	struct adm9240_data *data = dev_get_drvdata(dev); +	int reg; + +	switch (attr) { +	case hwmon_in_min: +		reg = ADM9240_REG_IN_MIN(channel); +		break; +	case hwmon_in_max: +		reg = ADM9240_REG_IN(channel); +		break; +	default: +		return -EOPNOTSUPP; +	} +	return regmap_write(data->regmap, reg, IN_TO_REG(val, channel)); +} + +static int adm9240_fan_read(struct device *dev, u32 attr, int channel, long *val) +{ +	struct adm9240_data *data = dev_get_drvdata(dev); +	unsigned int regval; +	int err; + +	switch (attr) { +	case hwmon_fan_input: +		err = regmap_read(data->regmap, ADM9240_REG_FAN(channel), ®val); +		if (err < 0) +			return err; +		if (regval == 255 && data->fan_div[channel] < 3) { +			/* adjust fan clock divider on overflow */ +			err = adm9240_write_fan_div(data, channel, +						    ++data->fan_div[channel]); +			if (err) +				return err; +		} +		*val = FAN_FROM_REG(regval, BIT(data->fan_div[channel])); +		break; +	case hwmon_fan_div: +		*val = BIT(data->fan_div[channel]); +		break; +	case hwmon_fan_min: +		err = regmap_read(data->regmap, ADM9240_REG_FAN_MIN(channel), ®val); +		if (err < 0) +			return err; +		*val = FAN_FROM_REG(regval, BIT(data->fan_div[channel])); +		break; +	case hwmon_fan_alarm: +		err = regmap_read(data->regmap, ADM9240_REG_INT(0), ®val); +		if (err < 0) +			return err; +		*val = !!(regval & BIT(channel + 6)); +		break; +	default: +		return -EOPNOTSUPP; +	} +	return 0; +} + +static int adm9240_fan_write(struct device *dev, u32 attr, int channel, long val) +{ +	struct adm9240_data *data = dev_get_drvdata(dev); +	int err; + +	switch (attr) { +	case hwmon_fan_min: +		err = adm9240_fan_min_write(data, channel, val); +		if (err < 0) +			return err; +		break; +	default: +		return -EOPNOTSUPP; +	} +	return 0; +} + +static int adm9240_temp_read(struct device *dev, u32 attr, int channel, long *val) +{ +	struct adm9240_data *data = dev_get_drvdata(dev); +	unsigned int regval; +	int err, temp; + +	switch (attr) { +	case hwmon_temp_input: +		err = regmap_read(data->regmap, ADM9240_REG_TEMP, ®val); +		if (err < 0) +			return err; +		temp = regval << 1; +		err = regmap_read(data->regmap, ADM9240_REG_TEMP_CONF, ®val); +		if (err < 0) +			return err; +		temp |= regval >> 7; +		*val = sign_extend32(temp, 8) * 500; +		break; +	case hwmon_temp_max: +		err = regmap_read(data->regmap, ADM9240_REG_TEMP_MAX(0), ®val); +		if (err < 0) +			return err; +		*val = (s8)regval * 1000; +		break; +	case hwmon_temp_max_hyst: +		err = regmap_read(data->regmap, ADM9240_REG_TEMP_MAX(1), ®val); +		if (err < 0) +			return err; +		*val = (s8)regval * 1000; +		break; +	case hwmon_temp_alarm: +		err = regmap_read(data->regmap, ADM9240_REG_INT(0), ®val); +		if (err < 0) +			return err; +		*val = !!(regval & BIT(4)); +		break; +	default: +		return -EOPNOTSUPP; +	} +	return 0; +} + +static int adm9240_temp_write(struct device *dev, u32 attr, int channel, long val) +{ +	struct adm9240_data *data = dev_get_drvdata(dev); +	int reg; + +	switch (attr) { +	case hwmon_temp_max: +		reg = ADM9240_REG_TEMP_MAX(0); +		break; +	case hwmon_temp_max_hyst: +		reg = ADM9240_REG_TEMP_MAX(1); +		break; +	default: +		return -EOPNOTSUPP; +	} +	return regmap_write(data->regmap, reg, TEMP_TO_REG(val)); +} + +static int adm9240_read(struct device *dev, enum hwmon_sensor_types type, u32 attr, +			int channel, long *val) +{ +	switch (type) { +	case hwmon_chip: +		return adm9240_chip_read(dev, attr, val); +	case hwmon_intrusion: +		return adm9240_intrusion_read(dev, attr, val); +	case hwmon_in: +		return adm9240_in_read(dev, attr, channel, val); +	case hwmon_fan: +		return adm9240_fan_read(dev, attr, channel, val); +	case hwmon_temp: +		return adm9240_temp_read(dev, attr, channel, val); +	default: +		return -EOPNOTSUPP; +	} +} + +static int adm9240_write(struct device *dev, enum hwmon_sensor_types type, u32 attr, +			 int channel, long val) +{ +	switch (type) { +	case hwmon_intrusion: +		return adm9240_intrusion_write(dev, attr, val); +	case hwmon_in: +		return adm9240_in_write(dev, attr, channel, val); +	case hwmon_fan: +		return adm9240_fan_write(dev, attr, channel, val); +	case hwmon_temp: +		return adm9240_temp_write(dev, attr, channel, val); +	default: +		return -EOPNOTSUPP; +	} +} + +static umode_t adm9240_is_visible(const void *_data, enum hwmon_sensor_types type, +				  u32 attr, int channel) +{ +	umode_t mode = 0; + +	switch (type) { +	case hwmon_chip: +		switch (attr) { +		case hwmon_chip_alarms: +			mode = 0444; +			break; +		default: +			break; +		} +		break; +	case hwmon_intrusion: +		switch (attr) { +		case hwmon_intrusion_alarm: +			mode = 0644; +			break; +		default: +			break; +		} +		break; +	case hwmon_temp: +		switch (attr) { +		case hwmon_temp: +		case hwmon_temp_alarm: +			mode = 0444; +			break; +		case hwmon_temp_max: +		case hwmon_temp_max_hyst: +			mode = 0644; +			break; +		default: +			break; +		} +		break; +	case hwmon_fan: +		switch (attr) { +		case hwmon_fan_input: +		case hwmon_fan_div: +		case hwmon_fan_alarm: +			mode = 0444; +			break; +		case hwmon_fan_min: +			mode = 0644; +			break; +		default: +			break; +		} +		break; +	case hwmon_in: +		switch (attr) { +		case hwmon_in_input: +		case hwmon_in_alarm: +			mode = 0444; +			break; +		case hwmon_in_min: +		case hwmon_in_max: +			mode = 0644; +			break; +		default: +			break; +		} +		break; +	default: +		break; +	} +	return mode; +} + +static const struct hwmon_ops adm9240_hwmon_ops = { +	.is_visible = adm9240_is_visible, +	.read = adm9240_read, +	.write = adm9240_write, +}; + +static const struct hwmon_channel_info *adm9240_info[] = { +	HWMON_CHANNEL_INFO(chip, HWMON_C_ALARMS), +	HWMON_CHANNEL_INFO(intrusion, HWMON_INTRUSION_ALARM), +	HWMON_CHANNEL_INFO(temp, +			   HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_MAX_HYST | HWMON_T_ALARM), +	HWMON_CHANNEL_INFO(in, +			   HWMON_I_INPUT | HWMON_I_MIN | HWMON_I_MAX | HWMON_I_ALARM, +			   HWMON_I_INPUT | HWMON_I_MIN | HWMON_I_MAX | HWMON_I_ALARM, +			   HWMON_I_INPUT | HWMON_I_MIN | HWMON_I_MAX | HWMON_I_ALARM, +			   HWMON_I_INPUT | HWMON_I_MIN | HWMON_I_MAX | HWMON_I_ALARM, +			   HWMON_I_INPUT | HWMON_I_MIN | HWMON_I_MAX | HWMON_I_ALARM, +			   HWMON_I_INPUT | HWMON_I_MIN | HWMON_I_MAX | HWMON_I_ALARM), +	HWMON_CHANNEL_INFO(fan, +			   HWMON_F_INPUT | HWMON_F_MIN | HWMON_F_DIV | HWMON_F_ALARM, +			   HWMON_F_INPUT | HWMON_F_MIN | HWMON_F_DIV | HWMON_F_ALARM), +	NULL +}; + +static const struct hwmon_chip_info adm9240_chip_info = { +	.ops = &adm9240_hwmon_ops, +	.info = adm9240_info, +}; + +static bool adm9240_volatile_reg(struct device *dev, unsigned int reg) +{ +	switch (reg) { +	case ADM9240_REG_IN(0) ... ADM9240_REG_IN(5): +	case ADM9240_REG_FAN(0) ... ADM9240_REG_FAN(1): +	case ADM9240_REG_INT(0) ... ADM9240_REG_INT(1): +	case ADM9240_REG_TEMP: +	case ADM9240_REG_TEMP_CONF: +	case ADM9240_REG_VID_FAN_DIV: +	case ADM9240_REG_VID4: +	case ADM9240_REG_ANALOG_OUT: +		return true; +	default: +		return false; +	} +} +  static const struct regmap_config adm9240_regmap_config = {  	.reg_bits = 8,  	.val_bits = 8,  	.use_single_read = true,  	.use_single_write = true, +	.volatile_reg = adm9240_volatile_reg,  }; -static int adm9240_probe(struct i2c_client *new_client) +static int adm9240_probe(struct i2c_client *client)  { -	struct device *dev = &new_client->dev; +	struct device *dev = &client->dev;  	struct device *hwmon_dev;  	struct adm9240_data *data;  	int err; @@ -841,20 +784,19 @@ static int adm9240_probe(struct i2c_client *new_client)  	if (!data)  		return -ENOMEM; -	data->client = new_client; +	data->dev = dev;  	mutex_init(&data->update_lock); -	data->regmap = devm_regmap_init_i2c(new_client, &adm9240_regmap_config); +	data->regmap = devm_regmap_init_i2c(client, &adm9240_regmap_config);  	if (IS_ERR(data->regmap))  		return PTR_ERR(data->regmap); -	err = adm9240_init_client(new_client, data); +	err = adm9240_init_client(data);  	if (err < 0)  		return err; -	hwmon_dev = devm_hwmon_device_register_with_groups(dev, -							   new_client->name, -							   data, -							   adm9240_groups); +	hwmon_dev = devm_hwmon_device_register_with_info(dev, client->name, data, +							 &adm9240_chip_info, +							 adm9240_groups);  	return PTR_ERR_OR_ZERO(hwmon_dev);  } | 
