[lm-sensors] [RFC v2] [PATCH] hwmon: Add LTC4245 driver

Hans de Goede hdegoede at redhat.com
Wed Oct 15 10:22:47 CEST 2008


Ira Snyder wrote:
> Add Linux support for the Linear Technology LTC4245 Multiple Supply Hot
> Swap controller I2C monitoring interface.
> 
> Signed-off-by: Ira W. Snyder <iws at ovro.caltech.edu>
> ---
> 
> I've incorporated the suggestions from my first posting of this driver.
> 
> I decided to add current and power readings for the Vee (-12v) voltage,
> as well as the 12v, 5v, and 3v mentioned in the suggestions. This seemed
> reasonable to me.
> 

Good.

> I'd appreciate any more suggestions you may have. :)
> 

See below :)

<snip>

> +power1_input		12v power usage (mW)
> +power2_input		5v  power usage (mW)
> +power3_input		3v  power usage (mW)
> +power4_input		Vee (-12v) power usage (mW)
> +

These should be in micro Watt so μW not mW, I know this is not consistentent 
with all other input's being in milli something, but the first piece of 
supported power measuring hardware we had has sub milliWatt precision, hence 
this decision.

<snip>

 > +power1_alarm		12v power bad alarm
 > +power2_alarm		5v  power bad alarm
 > +power3_alarm		3v  power bad alarm
 > +power4_alarm		Vee (-12v) power bad alarm

Ah I missed these the first time, looking at the data sheet these are actually 
the output voltages going under a specified treshold. So please rename these to 
in5_min_alarm to in8_min_alarm

<snip>

> +/* Return the voltage from the given register in millivolts */
> +static u32 ltc4245_get_voltage(struct device *dev, u8 reg)
> +{
> +	struct ltc4245_data *data = ltc4245_update_device(dev);
> +	const s32 val = data->vregs[reg - 0x10];
> +	const u8 regval = val;
> +	u32 mv = 0;
> +
> +	if (unlikely(val < 0))
> +		return 0;
> +
> +	switch (reg) {
> +	case LTC4245_12VIN:
> +	case LTC4245_12VOUT:
> +		mv = regval * 55;
> +		break;
> +	case LTC4245_12VSENSE:
> +		mv = regval * 250 / 1000;

You are loosing precision here, regval needs to change 4 steps for mv to change 
one. I think it is better todo the current calculation in one step in 
get_current() with a comment which contains the multi step floanting point 
human readable form. In any case you must change things so that this precision 
no longer gets lost.

> +		break;
> +	case LTC4245_5VIN:
> +	case LTC4245_5VOUT:
> +		mv = regval * 22;
> +		break;
> +	case LTC4245_5VSENSE:
> +		mv = regval * 125 / 1000;
> +		break;

Idem.

> +	case LTC4245_3VIN:
> +	case LTC4245_3VOUT:
> +		mv = regval * 15;
> +		break;
> +	case LTC4245_3VSENSE:
> +		mv = regval * 125 / 1000;
> +		break;

Idem.

> +	case LTC4245_VEEIN:
> +	case LTC4245_VEEOUT:
> +		mv = regval * 55;
> +		break;
> +	case LTC4245_VEESENSE:
> +		mv = regval * 250 / 1000;
> +		break;

Guess what? Idem :)

> +	case LTC4245_GPIOADC1:
> +	case LTC4245_GPIOADC2:
> +	case LTC4245_GPIOADC3:
> +		mv = regval * 10;
> +		break;
> +	default:
> +		WARN_ON_ONCE(1);
> +		break;
> +	}
> +
> +	return mv;
> +}
> +
> +/* Return the current in the given sense register in milliAmperes */
> +static u32 ltc4245_get_current(struct device *dev, u8 reg)
> +{
> +	const u32 voltage = ltc4245_get_voltage(dev, reg);
> +
> +	switch (reg) {
> +	case LTC4245_12VSENSE:
> +		return voltage / 50;

This returns the Amperage in Amperes not in milli Amperes. milli Volt divided 
by milli Ohm gives Ampere not milli Ampere. So the correct calculation would be:

 > +		return voltage * 1000 / 50;

At which point the resolution loss described above becomes very relevant!

> +	case LTC4245_5VSENSE:
> +		/* Fixed point math: mV / 3.5 mOhm == mA */
> +		return (voltage * 35 + 5) / 10;
> +	case LTC4245_3VSENSE:
> +		/* Fixed point math: mV / 2.5 mOhm == mA */
> +		return (voltage * 25 + 5) / 10;
> +	case LTC4245_VEESENSE:
> +		return voltage / 100;

All 3 idem. Also why don't you add + 25 resp. + 50 for correct rounding in the 
+/- 12V cases?

> +static ssize_t ltc4245_show_voltage_alarm(struct device *dev,
> +					  struct device_attribute *da,
> +					  char *buf)
> +{
> +	struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
> +	struct ltc4245_data *data = ltc4245_update_device(dev);
> +	const s32 fault1 = data->cregs[LTC4245_FAULT1];
> +	int alarm = 0;
> +
> +	if (unlikely(fault1 < 0)) {
> +		memset(buf, 0, PAGE_SIZE); /* user should not see old data */
> +		return 0;
> +	}
> +
> +	switch (attr->index) {
> +	case LTC4245_12VIN:
> +		alarm = fault1 & (1 << 0);
> +		break;
> +	case LTC4245_5VIN:
> +		alarm = fault1 & (1 << 1);
> +		break;
> +	case LTC4245_3VIN:
> +		alarm = fault1 & (1 << 2);
> +		break;
> +	case LTC4245_VEEIN:
> +		alarm = fault1 & (1 << 3);
> +		break;

Why don't you just set attr->index to to the mask to test with, or the amount 
to shift the 1 and then get rid of this switch case?

> +	default:
> +		/* If we get here, the developer messed up */
> +		WARN_ON_ONCE(1);
> +		break;
> +	}
> +
> +	return snprintf(buf, PAGE_SIZE, "%d\n", alarm ? 1 : 0);
> +}
> +
> +static ssize_t ltc4245_show_current_alarm(struct device *dev,
> +					  struct device_attribute *da,
> +					  char *buf)
> +{
> +	struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
> +	struct ltc4245_data *data = ltc4245_update_device(dev);
> +	const s32 fault1 = data->cregs[LTC4245_FAULT1];
> +	int alarm = 0;
> +
> +	if (unlikely(fault1 < 0)) {
> +		memset(buf, 0, PAGE_SIZE); /* user should not see old data */
> +		return 0;
> +	}
> +
> +	switch (attr->index) {
> +	case LTC4245_12VSENSE:
> +		alarm = fault1 & (1 << 4);
> +		break;
> +	case LTC4245_5VSENSE:
> +		alarm = fault1 & (1 << 5);
> +		break;
> +	case LTC4245_3VSENSE:
> +		alarm = fault1 & (1 << 6);
> +		break;
> +	case LTC4245_VEESENSE:
> +		alarm = fault1 & (1 << 7);
> +		break;
> +	default:
> +		/* If we get here, the developer messed up */
> +		WARN_ON_ONCE(1);
> +		break;
> +	}
> +

Idem, note that this function is and most certainly becomes identical to
ltc4245_show_voltage_alarm, so please fold the 2 into 1.

> +	return snprintf(buf, PAGE_SIZE, "%d\n", alarm ? 1 : 0);
> +}
> +
> +static ssize_t ltc4245_show_power_alarm(struct device *dev,
> +					struct device_attribute *da,
> +					char *buf)
> +{
> +	struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
> +	struct ltc4245_data *data = ltc4245_update_device(dev);
> +	const s32 fault2 = data->cregs[LTC4245_FAULT2];
> +	int alarm = 0;
> +
> +	if (unlikely(fault2 < 0)) {
> +		memset(buf, 0, PAGE_SIZE); /* user should not see old data */
> +		return 0;
> +	}
> +
> +	switch (attr->index) {
> +	case LTC4245_12VOUT:
> +		alarm = fault2 & (1 << 0);
> +		break;
> +	case LTC4245_5VOUT:
> +		alarm = fault2 & (1 << 1);
> +		break;
> +	case LTC4245_3VOUT:
> +		alarm = fault2 & (1 << 2);
> +		break;
> +	case LTC4245_VEEOUT:
> +		alarm = fault2 & (1 << 3);
> +		break;

Idem, see below for some more comments about folding all 3 alarm functions into 1.

> +	default:
> +		/* If we get here, the developer messed up */
> +		WARN_ON_ONCE(1);
> +		break;
> +	}
> +
> +	return snprintf(buf, PAGE_SIZE, "%d\n", alarm ? 1 : 0);
> +}
> +
> +/* These macros are used below in constructing device attribute objects
> + * for use with sysfs_create_group() to make a sysfs device file
> + * for each register.
> + */
> +#define LTC4245_VOLTAGE_RO(name, ltc4245_cmd_idx) \
> +	static SENSOR_DEVICE_ATTR(name, S_IRUGO, \
> +	ltc4245_show_voltage, NULL, ltc4245_cmd_idx)
> +

Please combine this with LTC4245_VOLTAGE_ALARM into one LTC4245_VOLTAGE
define. Just pass in for example in1 as name and use string concatenation to 
get the in1_input and in1_min_alarm names, like this: "name ## _input" and 
"name ## _min_alarm" .

Also use a third macro argument for the index value for the alarm 
SENSOR_DEVICE_ATTR() so that you can directly pass in the shift value / mask 
for getting the alarm. Hmm, once you add the in4_min_alarm -> in8_min_alarm, 
you need to pass 2 things, which fault register to use and which mask to apply, 
better switch to SENSOR_DEVICE_ATTR2 then, which allows you to pass both an 
index and a nr.

> +#define LTC4245_CURRENT_RO(name, ltc4245_cmd_idx) \
> +	static SENSOR_DEVICE_ATTR(name, S_IRUGO, \
> +	ltc4245_show_current, NULL, ltc4245_cmd_idx)
> +

Idem combine with LTC4245_CURRENT_ALARM please.

> +#define LTC4245_POWER_RO(name, ltc4245_cmd_idx) \
> +	static SENSOR_DEVICE_ATTR(name, S_IRUGO, \
> +	ltc4245_show_power, NULL, ltc4245_cmd_idx)
> +

> +#define LTC4245_VOLTAGE_ALARM(name, ltc4245_cmd_idx) \
> +	static SENSOR_DEVICE_ATTR(name, S_IRUGO, \
> +	ltc4245_show_voltage_alarm, NULL, ltc4245_cmd_idx)
> +
> +#define LTC4245_CURRENT_ALARM(name, ltc4245_cmd_idx) \
> +	static SENSOR_DEVICE_ATTR(name, S_IRUGO, \
> +	ltc4245_show_current_alarm, NULL, ltc4245_cmd_idx)
> +
> +#define LTC4245_POWER_ALARM(name, ltc4245_cmd_idx) \
> +	static SENSOR_DEVICE_ATTR(name, S_IRUGO, \
> +	ltc4245_show_power_alarm, NULL, ltc4245_cmd_idx)
> +

These 3 should be removed then.

<snip>

Well still some work to do, but we are definitely getting there. I think the 
3th revision will be "perfect".

Regards,

Hans





More information about the lm-sensors mailing list