Re: [PATCH v7 2/2] hwmon: ina3221: Read channel input source info from DT

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On Thu, Sep 27, 2018 at 01:54:06PM -0700, Nicolin Chen wrote:
> An ina3221 chip has three input ports. Each port is used
> to measure the voltage and current of its input source.
> 
> The DT binding now has defined bindings for their input
> sources, so the driver should read these information and
> handle accordingly.
> 
> This patch adds a new structure of input source specific
> information including input source label, shunt resistor
> value and its connection status. It exposes these labels
> via in[123]_label sysfs nodes upon available, and also
> disables those channels where there are no input source
> being connected. Meanwhile, it also adds in[123]_enable
> sysfs nodes for users to get control of three channels,
> and returns -ENODATA code for any sensor read according
> to hwmon ABI.
> 
> Signed-off-by: Nicolin Chen <nicoleotsuka@xxxxxxxxx>
> ---
> Changelog
> v6->v7:
>  * N/A
> v5->v6:
>  * Removed the code of hiding disconnected channels
>  * Added in[123]_label sysfs nodes to control channels
>  * Added -ENODATA return for sensor read at disabled channels
> v4->v5:
>  * Replaced "shunt-resistor" with "shunt-resistor-micro-ohms"
>  * Replaced "input-label" with "label"
>  * Replaced "input-id" with "reg"
>  * Simplified is_visible() by using index of the attr
>  * Moved inX_label to index-0 and added comments for safety
> v3->v4:
>  * Added is_visible callback function to hide sysfs nodes
> v2->v3:
>  * N/A
> v1->v2:
>  * Added a structure for input source corresponding to DT bindings
>  * Moved shunt resistor value to the structure
>  * Defined in[123]_label sysfs nodes instead of name[123]_input
>  * Updated probe() function to get information from DT
>  * Updated ina3221 hwmon documentation for the labels
>  * Dropped dynamical group definition to keep all groups as they were
> 
>  Documentation/hwmon/ina3221 |   2 +
>  drivers/hwmon/ina3221.c     | 237 +++++++++++++++++++++++++++++++++---
>  2 files changed, 225 insertions(+), 14 deletions(-)
> 
> diff --git a/Documentation/hwmon/ina3221 b/Documentation/hwmon/ina3221
> index 0ff74854cb2e..4b82cbfb551c 100644
> --- a/Documentation/hwmon/ina3221
> +++ b/Documentation/hwmon/ina3221
> @@ -21,6 +21,8 @@ and power are calculated host-side from these.
>  Sysfs entries
>  -------------
>  
> +in[123]_label           Voltage channel labels
> +in[123]_enable          Voltage channel enable controls
>  in[123]_input           Bus voltage(mV) channels
>  curr[123]_input         Current(mA) measurement channels
>  shunt[123]_resistor     Shunt resistance(uOhm) channels
> diff --git a/drivers/hwmon/ina3221.c b/drivers/hwmon/ina3221.c
> index e6b49500c52a..5c16d78e7482 100644
> --- a/drivers/hwmon/ina3221.c
> +++ b/drivers/hwmon/ina3221.c
> @@ -41,6 +41,7 @@
>  #define INA3221_CONFIG_MODE_SHUNT	BIT(1)
>  #define INA3221_CONFIG_MODE_BUS		BIT(2)
>  #define INA3221_CONFIG_MODE_CONTINUOUS	BIT(3)
> +#define INA3221_CONFIG_CHx_EN(x)	BIT(14 - (x))
>  
>  #define INA3221_RSHUNT_DEFAULT		10000
>  
> @@ -75,6 +76,9 @@ enum ina3221_channels {
>  };
>  
>  static const unsigned int register_channel[] = {
> +	[INA3221_BUS1] = INA3221_CHANNEL1,
> +	[INA3221_BUS2] = INA3221_CHANNEL2,
> +	[INA3221_BUS3] = INA3221_CHANNEL3,
>  	[INA3221_SHUNT1] = INA3221_CHANNEL1,
>  	[INA3221_SHUNT2] = INA3221_CHANNEL2,
>  	[INA3221_SHUNT3] = INA3221_CHANNEL3,
> @@ -86,18 +90,93 @@ static const unsigned int register_channel[] = {
>  	[INA3221_WARN3] = INA3221_CHANNEL3,
>  };
>  
> +/**
> + * struct ina3221_input - channel input source specific information
> + * @label: label of channel input source
> + * @shunt_resistor: shunt resistor value of channel input source
> + * @disconnected: connection status of channel input source
> + */
> +struct ina3221_input {
> +	const char *label;
> +	int shunt_resistor;
> +	bool disconnected;
> +};
> +
>  /**
>   * struct ina3221_data - device specific information
>   * @regmap: Register map of the device
>   * @fields: Register fields of the device
> - * @shunt_resistors: Array of resistor values per channel
> + * @inputs: Array of channel input source specific structures
>   */
>  struct ina3221_data {
>  	struct regmap *regmap;
>  	struct regmap_field *fields[F_MAX_FIELDS];
> -	int shunt_resistors[INA3221_NUM_CHANNELS];
> +	struct ina3221_input inputs[INA3221_NUM_CHANNELS];
>  };
>  
> +static inline bool ina3221_is_enable(struct ina3221_data *ina, int channel)
> +{
> +	unsigned int config;
> +	int ret;
> +
> +	/* Return false to reject further read */
> +	ret = regmap_read(ina->regmap, INA3221_CONFIG, &config);
> +	if (ret)
> +		return false;
> +
> +	return (config & INA3221_CONFIG_CHx_EN(channel)) > 0;

I had asked you to either drop the "> 0" or use !!.

> +}
> +
> +static ssize_t ina3221_show_label(struct device *dev,
> +				  struct device_attribute *attr, char *buf)
> +{
> +	struct sensor_device_attribute *sd_attr = to_sensor_dev_attr(attr);
> +	struct ina3221_data *ina = dev_get_drvdata(dev);
> +	unsigned int channel = sd_attr->index;
> +	struct ina3221_input *input = &ina->inputs[channel];
> +
> +	return snprintf(buf, PAGE_SIZE, "%s\n", input->label);
> +}
> +
> +static ssize_t ina3221_show_enable(struct device *dev,
> +				   struct device_attribute *attr, char *buf)
> +{
> +	struct sensor_device_attribute *sd_attr = to_sensor_dev_attr(attr);
> +	struct ina3221_data *ina = dev_get_drvdata(dev);
> +	unsigned int channel = sd_attr->index;
> +
> +	return snprintf(buf, PAGE_SIZE, "%d\n",
> +			ina3221_is_enable(ina, channel));
> +}
> +
> +static ssize_t ina3221_set_enable(struct device *dev,
> +				  struct device_attribute *attr,
> +				  const char *buf, size_t count)
> +{
> +	struct sensor_device_attribute *sd_attr = to_sensor_dev_attr(attr);
> +	struct ina3221_data *ina = dev_get_drvdata(dev);
> +	unsigned int channel = sd_attr->index;
> +	unsigned int mask = INA3221_CONFIG_CHx_EN(channel);
> +	unsigned int config;
> +	int val, ret;
> +
> +	ret = kstrtoint(buf, 0, &val);
> +	if (ret)
> +		return ret;
> +
> +	/* inX_enable only accepts 1 for enabling or 0 for disabling */
> +	if (val != 0 && val != 1)
> +		return -EINVAL;
> +

I am quite sure I asked to use kstrtobool(). Did that get lost or do you have
some reason to not use it ?

I can understand if you don't want to change ina3221_is_enable() to
ina3221_is_enabled(), since that is POV, but I don't really understand
why you did not make the other changes. Am I missing something ?

Thanks,
Guenter

> +	config = val ? mask : 0;
> +
> +	ret = regmap_update_bits(ina->regmap, INA3221_CONFIG, mask, config);
> +	if (ret)
> +		return ret;
> +
> +	return count;
> +}
> +
>  static int ina3221_read_value(struct ina3221_data *ina, unsigned int reg,
>  			      int *val)
>  {
> @@ -120,8 +199,13 @@ static ssize_t ina3221_show_bus_voltage(struct device *dev,
>  	struct sensor_device_attribute *sd_attr = to_sensor_dev_attr(attr);
>  	struct ina3221_data *ina = dev_get_drvdata(dev);
>  	unsigned int reg = sd_attr->index;
> +	unsigned int channel = register_channel[reg];
>  	int val, voltage_mv, ret;
>  
> +	/* No data for read-only attribute if channel is disabled */
> +	if (!attr->store && !ina3221_is_enable(ina, channel))
> +		return -ENODATA;
> +
>  	ret = ina3221_read_value(ina, reg, &val);
>  	if (ret)
>  		return ret;
> @@ -138,8 +222,13 @@ static ssize_t ina3221_show_shunt_voltage(struct device *dev,
>  	struct sensor_device_attribute *sd_attr = to_sensor_dev_attr(attr);
>  	struct ina3221_data *ina = dev_get_drvdata(dev);
>  	unsigned int reg = sd_attr->index;
> +	unsigned int channel = register_channel[reg];
>  	int val, voltage_uv, ret;
>  
> +	/* No data for read-only attribute if channel is disabled */
> +	if (!attr->store && !ina3221_is_enable(ina, channel))
> +		return -ENODATA;
> +
>  	ret = ina3221_read_value(ina, reg, &val);
>  	if (ret)
>  		return ret;
> @@ -155,9 +244,14 @@ static ssize_t ina3221_show_current(struct device *dev,
>  	struct ina3221_data *ina = dev_get_drvdata(dev);
>  	unsigned int reg = sd_attr->index;
>  	unsigned int channel = register_channel[reg];
> -	int resistance_uo = ina->shunt_resistors[channel];
> +	struct ina3221_input *input = &ina->inputs[channel];
> +	int resistance_uo = input->shunt_resistor;
>  	int val, current_ma, voltage_nv, ret;
>  
> +	/* No data for read-only attribute if channel is disabled */
> +	if (!attr->store && !ina3221_is_enable(ina, channel))
> +		return -ENODATA;
> +
>  	ret = ina3221_read_value(ina, reg, &val);
>  	if (ret)
>  		return ret;
> @@ -176,7 +270,8 @@ static ssize_t ina3221_set_current(struct device *dev,
>  	struct ina3221_data *ina = dev_get_drvdata(dev);
>  	unsigned int reg = sd_attr->index;
>  	unsigned int channel = register_channel[reg];
> -	int resistance_uo = ina->shunt_resistors[channel];
> +	struct ina3221_input *input = &ina->inputs[channel];
> +	int resistance_uo = input->shunt_resistor;
>  	int val, current_ma, voltage_uv, ret;
>  
>  	ret = kstrtoint(buf, 0, &current_ma);
> @@ -209,11 +304,9 @@ static ssize_t ina3221_show_shunt(struct device *dev,
>  	struct sensor_device_attribute *sd_attr = to_sensor_dev_attr(attr);
>  	struct ina3221_data *ina = dev_get_drvdata(dev);
>  	unsigned int channel = sd_attr->index;
> -	unsigned int resistance_uo;
> -
> -	resistance_uo = ina->shunt_resistors[channel];
> +	struct ina3221_input *input = &ina->inputs[channel];
>  
> -	return snprintf(buf, PAGE_SIZE, "%d\n", resistance_uo);
> +	return snprintf(buf, PAGE_SIZE, "%d\n", input->shunt_resistor);
>  }
>  
>  static ssize_t ina3221_set_shunt(struct device *dev,
> @@ -223,6 +316,7 @@ static ssize_t ina3221_set_shunt(struct device *dev,
>  	struct sensor_device_attribute *sd_attr = to_sensor_dev_attr(attr);
>  	struct ina3221_data *ina = dev_get_drvdata(dev);
>  	unsigned int channel = sd_attr->index;
> +	struct ina3221_input *input = &ina->inputs[channel];
>  	int val;
>  	int ret;
>  
> @@ -232,7 +326,7 @@ static ssize_t ina3221_set_shunt(struct device *dev,
>  
>  	val = clamp_val(val, 1, INT_MAX);
>  
> -	ina->shunt_resistors[channel] = val;
> +	input->shunt_resistor = val;
>  
>  	return count;
>  }
> @@ -253,6 +347,22 @@ static ssize_t ina3221_show_alert(struct device *dev,
>  	return snprintf(buf, PAGE_SIZE, "%d\n", regval);
>  }
>  
> +/* input channel label */
> +static SENSOR_DEVICE_ATTR(in1_label, 0444,
> +		ina3221_show_label, NULL, INA3221_CHANNEL1);
> +static SENSOR_DEVICE_ATTR(in2_label, 0444,
> +		ina3221_show_label, NULL, INA3221_CHANNEL2);
> +static SENSOR_DEVICE_ATTR(in3_label, 0444,
> +		ina3221_show_label, NULL, INA3221_CHANNEL3);
> +
> +/* voltage channel enable */
> +static SENSOR_DEVICE_ATTR(in1_enable, 0644,
> +		ina3221_show_enable, ina3221_set_enable, INA3221_CHANNEL1);
> +static SENSOR_DEVICE_ATTR(in2_enable, 0644,
> +		ina3221_show_enable, ina3221_set_enable, INA3221_CHANNEL2);
> +static SENSOR_DEVICE_ATTR(in3_enable, 0644,
> +		ina3221_show_enable, ina3221_set_enable, INA3221_CHANNEL3);
> +
>  /* bus voltage */
>  static SENSOR_DEVICE_ATTR(in1_input, S_IRUGO,
>  		ina3221_show_bus_voltage, NULL, INA3221_BUS1);
> @@ -318,7 +428,9 @@ static SENSOR_DEVICE_ATTR(in6_input, S_IRUGO,
>  		ina3221_show_shunt_voltage, NULL, INA3221_SHUNT3);
>  
>  static struct attribute *ina3221_attrs[] = {
> -	/* channel 1 */
> +	/* channel 1 -- make sure label at first */
> +	&sensor_dev_attr_in1_label.dev_attr.attr,
> +	&sensor_dev_attr_in1_enable.dev_attr.attr,
>  	&sensor_dev_attr_in1_input.dev_attr.attr,
>  	&sensor_dev_attr_curr1_input.dev_attr.attr,
>  	&sensor_dev_attr_shunt1_resistor.dev_attr.attr,
> @@ -328,7 +440,9 @@ static struct attribute *ina3221_attrs[] = {
>  	&sensor_dev_attr_curr1_max_alarm.dev_attr.attr,
>  	&sensor_dev_attr_in4_input.dev_attr.attr,
>  
> -	/* channel 2 */
> +	/* channel 2 -- make sure label at first */
> +	&sensor_dev_attr_in2_label.dev_attr.attr,
> +	&sensor_dev_attr_in2_enable.dev_attr.attr,
>  	&sensor_dev_attr_in2_input.dev_attr.attr,
>  	&sensor_dev_attr_curr2_input.dev_attr.attr,
>  	&sensor_dev_attr_shunt2_resistor.dev_attr.attr,
> @@ -338,7 +452,9 @@ static struct attribute *ina3221_attrs[] = {
>  	&sensor_dev_attr_curr2_max_alarm.dev_attr.attr,
>  	&sensor_dev_attr_in5_input.dev_attr.attr,
>  
> -	/* channel 3 */
> +	/* channel 3 -- make sure label at first */
> +	&sensor_dev_attr_in3_label.dev_attr.attr,
> +	&sensor_dev_attr_in3_enable.dev_attr.attr,
>  	&sensor_dev_attr_in3_input.dev_attr.attr,
>  	&sensor_dev_attr_curr3_input.dev_attr.attr,
>  	&sensor_dev_attr_shunt3_resistor.dev_attr.attr,
> @@ -350,7 +466,30 @@ static struct attribute *ina3221_attrs[] = {
>  
>  	NULL,
>  };
> -ATTRIBUTE_GROUPS(ina3221);
> +
> +static umode_t ina3221_attr_is_visible(struct kobject *kobj,
> +				       struct attribute *attr, int n)
> +{
> +	const int max_attrs = ARRAY_SIZE(ina3221_attrs) - 1;
> +	const int num_attrs = max_attrs / INA3221_NUM_CHANNELS;
> +	struct device *dev = kobj_to_dev(kobj);
> +	struct ina3221_data *ina = dev_get_drvdata(dev);
> +	enum ina3221_channels channel = n / num_attrs;
> +	struct ina3221_input *input = &ina->inputs[channel];
> +	int index = n % num_attrs;
> +
> +	/* Hide label node if label is not provided */
> +	if (index == 0 && !input->label)
> +		return 0;
> +
> +	return attr->mode;
> +}
> +
> +static const struct attribute_group ina3221_group = {
> +	.is_visible = ina3221_attr_is_visible,
> +	.attrs = ina3221_attrs,
> +};
> +__ATTRIBUTE_GROUPS(ina3221);
>  
>  static const struct regmap_range ina3221_yes_ranges[] = {
>  	regmap_reg_range(INA3221_SHUNT1, INA3221_BUS3),
> @@ -370,6 +509,60 @@ static const struct regmap_config ina3221_regmap_config = {
>  	.volatile_table = &ina3221_volatile_table,
>  };
>  
> +static int ina3221_probe_child_from_dt(struct device *dev,
> +				       struct device_node *child,
> +				       struct ina3221_data *ina)
> +{
> +	struct ina3221_input *input;
> +	u32 val;
> +	int ret;
> +
> +	ret = of_property_read_u32(child, "reg", &val);
> +	if (ret) {
> +		dev_err(dev, "missing reg property of %s\n", child->name);
> +		return ret;
> +	} else if (val > INA3221_CHANNEL3) {
> +		dev_err(dev, "invalid reg %d of %s\n", val, child->name);
> +		return ret;
> +	}
> +
> +	input = &ina->inputs[val];
> +
> +	/* Log the disconnected channel input */
> +	if (!of_device_is_available(child)) {
> +		input->disconnected = true;
> +		return 0;
> +	}
> +
> +	/* Save the connected input label if available */
> +	of_property_read_string(child, "label", &input->label);
> +
> +	/* Overwrite default shunt resistor value optionally */
> +	if (!of_property_read_u32(child, "shunt-resistor-micro-ohms", &val))
> +		input->shunt_resistor = val;
> +
> +	return 0;
> +}
> +
> +static int ina3221_probe_from_dt(struct device *dev, struct ina3221_data *ina)
> +{
> +	const struct device_node *np = dev->of_node;
> +	struct device_node *child;
> +	int ret;
> +
> +	/* Compatible with non-DT platforms */
> +	if (!np)
> +		return 0;
> +
> +	for_each_child_of_node(np, child) {
> +		ret = ina3221_probe_child_from_dt(dev, child, ina);
> +		if (ret)
> +			return ret;
> +	}
> +
> +	return 0;
> +}
> +
>  static int ina3221_probe(struct i2c_client *client,
>  			 const struct i2c_device_id *id)
>  {
> @@ -377,6 +570,7 @@ static int ina3221_probe(struct i2c_client *client,
>  	struct ina3221_data *ina;
>  	struct device *hwmon_dev;
>  	int i, ret;
> +	u16 mask;
>  
>  	ina = devm_kzalloc(dev, sizeof(*ina), GFP_KERNEL);
>  	if (!ina)
> @@ -399,7 +593,13 @@ static int ina3221_probe(struct i2c_client *client,
>  	}
>  
>  	for (i = 0; i < INA3221_NUM_CHANNELS; i++)
> -		ina->shunt_resistors[i] = INA3221_RSHUNT_DEFAULT;
> +		ina->inputs[i].shunt_resistor = INA3221_RSHUNT_DEFAULT;
> +
> +	ret = ina3221_probe_from_dt(dev, ina);
> +	if (ret) {
> +		dev_err(dev, "Unable to probe from device treee\n");
> +		return ret;
> +	}
>  
>  	ret = regmap_field_write(ina->fields[F_RST], true);
>  	if (ret) {
> @@ -407,6 +607,15 @@ static int ina3221_probe(struct i2c_client *client,
>  		return ret;
>  	}
>  
> +	/* Disable channels if their inputs are disconnected */
> +	for (i = 0, mask = 0; i < INA3221_NUM_CHANNELS; i++) {
> +		if (ina->inputs[i].disconnected)
> +			mask |= INA3221_CONFIG_CHx_EN(i);
> +	}
> +	ret = regmap_update_bits(ina->regmap, INA3221_CONFIG, mask, 0);
> +	if (ret)
> +		return ret;
> +
>  	hwmon_dev = devm_hwmon_device_register_with_groups(dev,
>  							   client->name,
>  							   ina, ina3221_groups);
> -- 
> 2.17.1
> 



[Index of Archives]     [Device Tree Compilter]     [Device Tree Spec]     [Linux Driver Backports]     [Video for Linux]     [Linux USB Devel]     [Linux PCI Devel]     [Linux Audio Users]     [Linux Kernel]     [Linux SCSI]     [XFree86]     [Yosemite Backpacking]


  Powered by Linux