diff mbox series

[2/2] hwmon: ina3221: Add enable sysfs nodes

Message ID 20180926064245.4091-3-nicoleotsuka@gmail.com (mailing list archive)
State Superseded
Headers show
Series hwmon: ina3221: Add power and enable sysfs nodes | expand

Commit Message

Nicolin Chen Sept. 26, 2018, 6:42 a.m. UTC
The inX_enable interface allows user space to enable or disable
the corresponding channel. Meanwhile, according to hwmon ABI, a
disabled channel/sensor should return -ENODATA as a read result.

However, there're configurable nodes sharing the same __show()
functions. So this change also adds to check if the attribute is
read-only to make sure it's not reading a configuration but the
sensor data.

Signed-off-by: Nicolin Chen <nicoleotsuka@gmail.com>
---
 Documentation/hwmon/ina3221 |  1 +
 drivers/hwmon/ina3221.c     | 85 +++++++++++++++++++++++++++++++++++++
 2 files changed, 86 insertions(+)

Comments

Guenter Roeck Sept. 26, 2018, 1:06 p.m. UTC | #1
Hi Nicolin,

On 09/25/2018 11:42 PM, Nicolin Chen wrote:
> The inX_enable interface allows user space to enable or disable
> the corresponding channel. Meanwhile, according to hwmon ABI, a
> disabled channel/sensor should return -ENODATA as a read result.
> 
> However, there're configurable nodes sharing the same __show()
> functions. So this change also adds to check if the attribute is
> read-only to make sure it's not reading a configuration but the
> sensor data.
> 

One necessary high level change I don't see below: With this change,
we should no longer drop a channel entirely if it is disabled from
devicetree. All channels should be visible but report -ENODATA if
disabled. In other words, it should be possible for the 'enable' flag
to override settings in DT.

> Signed-off-by: Nicolin Chen <nicoleotsuka@gmail.com>
> ---
>   Documentation/hwmon/ina3221 |  1 +
>   drivers/hwmon/ina3221.c     | 85 +++++++++++++++++++++++++++++++++++++
>   2 files changed, 86 insertions(+)
> 
> diff --git a/Documentation/hwmon/ina3221 b/Documentation/hwmon/ina3221
> index 6be64b553cd0..1aa32366d8aa 100644
> --- a/Documentation/hwmon/ina3221
> +++ b/Documentation/hwmon/ina3221
> @@ -23,6 +23,7 @@ Sysfs entries
>   
>   in[123]_input           Bus voltage(mV) channels
>   in[123]_label           Voltage channel labels
> +in[123]_enable          Voltage channel enable controls
>   curr[123]_input         Current(mA) measurement channels
>   shunt[123]_resistor     Shunt resistance(uOhm) channels
>   curr[123]_crit          Critical alert current(mA) setting, activates the
> diff --git a/drivers/hwmon/ina3221.c b/drivers/hwmon/ina3221.c
> index 5890a2da3bfe..1be2d062a19e 100644
> --- a/drivers/hwmon/ina3221.c
> +++ b/drivers/hwmon/ina3221.c
> @@ -78,6 +78,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,
> @@ -113,6 +116,19 @@ struct ina3221_data {
>   	struct ina3221_input inputs[INA3221_NUM_CHANNELS];
>   };
>   
> +static inline bool ina3221_is_enable(struct ina3221_data *ina, int channel)

s/is_enable/is_enabled/, maybe ?

> +{
> +	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;

The "> 0" is unnecessary. Conversion to bool is automatic. If you want to make
it explicit, please use

	return !!(config & INA3221_CONFIG_CHx_EN(channel));

It should not be necessary to re-read the value from the chip all the time.
I would suggest to cache the value in the 'disabled' variable.

> +}
> +
>   static ssize_t ina3221_show_label(struct device *dev,
>   				  struct device_attribute *attr, char *buf)
>   {
> @@ -124,6 +140,45 @@ static ssize_t ina3221_show_label(struct device *dev,
>   	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;
> +
Just use kstrtobool().

> +	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)
>   {
> @@ -146,8 +201,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;
> @@ -164,8 +224,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;
> @@ -201,8 +266,13 @@ static ssize_t ina3221_show_current(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 current_ma, ret;
>   
> +	/* No data for read-only attribute if channel is disabled */
> +	if (!attr->store && !ina3221_is_enable(ina, channel))
> +		return -ENODATA;
> +
>   	ret = __ina3221_show_current(ina, reg, &current_ma);
>   	if (ret)
>   		return ret;
> @@ -318,6 +388,10 @@ static ssize_t ina3221_show_power(struct device *dev,
>   	int val, current_ma, voltage_mv, ret;
>   	s64 power_uw;
>   
> +	/* No data for read-only attribute if channel is disabled */
> +	if (!attr->store && !ina3221_is_enable(ina, channel))
> +		return -ENODATA;
> +
>   	/* Read bus voltage */
>   	ret = ina3221_read_value(ina, reg_bus, &val);
>   	if (ret)
> @@ -377,6 +451,14 @@ static SENSOR_DEVICE_ATTR(in2_label, 0444,
>   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);
> @@ -460,6 +542,7 @@ static SENSOR_DEVICE_ATTR(power3_crit, 0644,
>   static struct attribute *ina3221_attrs[] = {
>   	/* 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,
> @@ -473,6 +556,7 @@ static struct attribute *ina3221_attrs[] = {
>   
>   	/* 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,
> @@ -486,6 +570,7 @@ static struct attribute *ina3221_attrs[] = {
>   
>   	/* 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,
>
Nicolin Chen Sept. 26, 2018, 6:02 p.m. UTC | #2
On Wed, Sep 26, 2018 at 06:06:32AM -0700, Guenter Roeck wrote:
> On 09/25/2018 11:42 PM, Nicolin Chen wrote:
> > The inX_enable interface allows user space to enable or disable
> > the corresponding channel. Meanwhile, according to hwmon ABI, a
> > disabled channel/sensor should return -ENODATA as a read result.
> > 
> > However, there're configurable nodes sharing the same __show()
> > functions. So this change also adds to check if the attribute is
> > read-only to make sure it's not reading a configuration but the
> > sensor data.
 
> One necessary high level change I don't see below: With this change,
> we should no longer drop a channel entirely if it is disabled from
> devicetree. All channels should be visible but report -ENODATA if
> disabled. In other words, it should be possible for the 'enable' flag
> to override settings in DT.

Hmm...I don't feel so convinced here. The status in DT binding isn't
exactly a setting but a physical status: if a hardware design leaves
a channel to be disconnected, I don't really see a point in enabling
it in the runtime. Or maybe you can shed some light on it?

Meanwhile, I believe the enable nodes are necessary in either way as
users could decide to disable the connected channels, based on their
use cases, to save power.

Thanks
Nicolin
Guenter Roeck Sept. 26, 2018, 7:58 p.m. UTC | #3
Nicolin,

On Wed, Sep 26, 2018 at 11:02:44AM -0700, Nicolin Chen wrote:
> On Wed, Sep 26, 2018 at 06:06:32AM -0700, Guenter Roeck wrote:
> > On 09/25/2018 11:42 PM, Nicolin Chen wrote:
> > > The inX_enable interface allows user space to enable or disable
> > > the corresponding channel. Meanwhile, according to hwmon ABI, a
> > > disabled channel/sensor should return -ENODATA as a read result.
> > > 
> > > However, there're configurable nodes sharing the same __show()
> > > functions. So this change also adds to check if the attribute is
> > > read-only to make sure it's not reading a configuration but the
> > > sensor data.
>  
> > One necessary high level change I don't see below: With this change,
> > we should no longer drop a channel entirely if it is disabled from
> > devicetree. All channels should be visible but report -ENODATA if
> > disabled. In other words, it should be possible for the 'enable' flag
> > to override settings in DT.
> 
> Hmm...I don't feel so convinced here. The status in DT binding isn't
> exactly a setting but a physical status: if a hardware design leaves
> a channel to be disconnected, I don't really see a point in enabling
> it in the runtime. Or maybe you can shed some light on it?
> 

You are making an assumption from your use case. It might as well be that
some or all channels are disabled in DT by default to conserve power,
not because they are disconnected.

> Meanwhile, I believe the enable nodes are necessary in either way as
> users could decide to disable the connected channels, based on their
> use cases, to save power.
> 
Agreed, though I would not say "necessary". "Useful" seems to be more
appropriate.

Thanks,
Guenter
Nicolin Chen Sept. 26, 2018, 8:25 p.m. UTC | #4
Hello,

On Wed, Sep 26, 2018 at 12:58:17PM -0700, Guenter Roeck wrote:
> On Wed, Sep 26, 2018 at 11:02:44AM -0700, Nicolin Chen wrote:
> > On Wed, Sep 26, 2018 at 06:06:32AM -0700, Guenter Roeck wrote:
> > > On 09/25/2018 11:42 PM, Nicolin Chen wrote:
> > > > The inX_enable interface allows user space to enable or disable
> > > > the corresponding channel. Meanwhile, according to hwmon ABI, a
> > > > disabled channel/sensor should return -ENODATA as a read result.
> > > > 
> > > > However, there're configurable nodes sharing the same __show()
> > > > functions. So this change also adds to check if the attribute is
> > > > read-only to make sure it's not reading a configuration but the
> > > > sensor data.
> >  
> > > One necessary high level change I don't see below: With this change,
> > > we should no longer drop a channel entirely if it is disabled from
> > > devicetree. All channels should be visible but report -ENODATA if
> > > disabled. In other words, it should be possible for the 'enable' flag
> > > to override settings in DT.
> > 
> > Hmm...I don't feel so convinced here. The status in DT binding isn't
> > exactly a setting but a physical status: if a hardware design leaves
> > a channel to be disconnected, I don't really see a point in enabling
> > it in the runtime. Or maybe you can shed some light on it?
> > 
> 
> You are making an assumption from your use case. It might as well be that
> some or all channels are disabled in DT by default to conserve power,
> not because they are disconnected.

I think I probably should update my DT binding somehow to say it
explicitly that the property should be only used in cases of the
physical disconnections, although I feel the current binding "no
input source" already has the same meaning.

In my opinion, disabling channels in DT to save power isn't very
plausible, because it sounds more likely a user decision, while,
as we know, DT merely describes the hardware design.

Otherwise, if we want something like a setting for this purpose,
we should probably use a different property for DT binding, bool
type "disable-on-boot" for example.

> > Meanwhile, I believe the enable nodes are necessary in either way as
> > users could decide to disable the connected channels, based on their
> > use cases, to save power.
> > 
> Agreed, though I would not say "necessary". "Useful" seems to be more
> appropriate.

Yea..
Guenter Roeck Sept. 26, 2018, 8:44 p.m. UTC | #5
On Wed, Sep 26, 2018 at 01:25:20PM -0700, Nicolin Chen wrote:
> Hello,
> 
> On Wed, Sep 26, 2018 at 12:58:17PM -0700, Guenter Roeck wrote:
> > On Wed, Sep 26, 2018 at 11:02:44AM -0700, Nicolin Chen wrote:
> > > On Wed, Sep 26, 2018 at 06:06:32AM -0700, Guenter Roeck wrote:
> > > > On 09/25/2018 11:42 PM, Nicolin Chen wrote:
> > > > > The inX_enable interface allows user space to enable or disable
> > > > > the corresponding channel. Meanwhile, according to hwmon ABI, a
> > > > > disabled channel/sensor should return -ENODATA as a read result.
> > > > > 
> > > > > However, there're configurable nodes sharing the same __show()
> > > > > functions. So this change also adds to check if the attribute is
> > > > > read-only to make sure it's not reading a configuration but the
> > > > > sensor data.
> > >  
> > > > One necessary high level change I don't see below: With this change,
> > > > we should no longer drop a channel entirely if it is disabled from
> > > > devicetree. All channels should be visible but report -ENODATA if
> > > > disabled. In other words, it should be possible for the 'enable' flag
> > > > to override settings in DT.
> > > 
> > > Hmm...I don't feel so convinced here. The status in DT binding isn't
> > > exactly a setting but a physical status: if a hardware design leaves
> > > a channel to be disconnected, I don't really see a point in enabling
> > > it in the runtime. Or maybe you can shed some light on it?
> > > 
> > 
> > You are making an assumption from your use case. It might as well be that
> > some or all channels are disabled in DT by default to conserve power,
> > not because they are disconnected.
> 
> I think I probably should update my DT binding somehow to say it
> explicitly that the property should be only used in cases of the
> physical disconnections, although I feel the current binding "no
> input source" already has the same meaning.
> 
> In my opinion, disabling channels in DT to save power isn't very
> plausible, because it sounds more likely a user decision, while,
> as we know, DT merely describes the hardware design.
> 

I try to avoid making such assumptions. All I know is that I'll have
to deal with the fallout if a single person wants to use the property
differently. This is similar to disabling an entire subsystem in DT
because it isn't used in a specific system - say, a SPI or I2C bus
which has nothing connected on some shipping hardware, even though
the board has a connector for it. Your argument is that one shall
not use the status property do disable loading the driver, and that
one must not remove a set of properties for unused hardware either.
That doesn't sound very realistic to me.

Point is that I don't _know_ how this is going to be used, so I'd
rather keep it flexible.

Guenter

> Otherwise, if we want something like a setting for this purpose,
> we should probably use a different property for DT binding, bool
> type "disable-on-boot" for example.
> 
> > > Meanwhile, I believe the enable nodes are necessary in either way as
> > > users could decide to disable the connected channels, based on their
> > > use cases, to save power.
> > > 
> > Agreed, though I would not say "necessary". "Useful" seems to be more
> > appropriate.
> 
> Yea..
Nicolin Chen Sept. 26, 2018, 9:55 p.m. UTC | #6
On Wed, Sep 26, 2018 at 01:44:55PM -0700, Guenter Roeck wrote:
> On Wed, Sep 26, 2018 at 01:25:20PM -0700, Nicolin Chen wrote:
> > Hello,
> > 
> > On Wed, Sep 26, 2018 at 12:58:17PM -0700, Guenter Roeck wrote:
> > > On Wed, Sep 26, 2018 at 11:02:44AM -0700, Nicolin Chen wrote:
> > > > On Wed, Sep 26, 2018 at 06:06:32AM -0700, Guenter Roeck wrote:
> > > > > On 09/25/2018 11:42 PM, Nicolin Chen wrote:
> > > > > > The inX_enable interface allows user space to enable or disable
> > > > > > the corresponding channel. Meanwhile, according to hwmon ABI, a
> > > > > > disabled channel/sensor should return -ENODATA as a read result.
> > > > > > 
> > > > > > However, there're configurable nodes sharing the same __show()
> > > > > > functions. So this change also adds to check if the attribute is
> > > > > > read-only to make sure it's not reading a configuration but the
> > > > > > sensor data.
> > > >  
> > > > > One necessary high level change I don't see below: With this change,
> > > > > we should no longer drop a channel entirely if it is disabled from
> > > > > devicetree. All channels should be visible but report -ENODATA if
> > > > > disabled. In other words, it should be possible for the 'enable' flag
> > > > > to override settings in DT.
> > > > 
> > > > Hmm...I don't feel so convinced here. The status in DT binding isn't
> > > > exactly a setting but a physical status: if a hardware design leaves
> > > > a channel to be disconnected, I don't really see a point in enabling
> > > > it in the runtime. Or maybe you can shed some light on it?
> > > > 
> > > 
> > > You are making an assumption from your use case. It might as well be that
> > > some or all channels are disabled in DT by default to conserve power,
> > > not because they are disconnected.
> > 
> > I think I probably should update my DT binding somehow to say it
> > explicitly that the property should be only used in cases of the
> > physical disconnections, although I feel the current binding "no
> > input source" already has the same meaning.
> > 
> > In my opinion, disabling channels in DT to save power isn't very
> > plausible, because it sounds more likely a user decision, while,
> > as we know, DT merely describes the hardware design.
> > 
> 
> I try to avoid making such assumptions. All I know is that I'll have
> to deal with the fallout if a single person wants to use the property
> differently.

I can understand your point (or pain lol). But I believe in such
a case, DT maintainers should reject such a DT change. Let's say
if this kinda "default setting" in DT is allowable, other things
such as having a default mode setting between polling or one-shot
modes, and as default critical current settings would be able to
put into DT. But we know that these would be rejected as a reason
of "not being hardware design but a user decision".

> This is similar to disabling an entire subsystem in DT
> because it isn't used in a specific system - say, a SPI or I2C bus
> which has nothing connected on some shipping hardware, even though
> the board has a connector for it. Your argument is that one shall
> not use the status property do disable loading the driver, and that
> one must not remove a set of properties for unused hardware either.
> That doesn't sound very realistic to me.

SPI/I2C is a good example comparing to my case. And you do make
a point. In that case, I think DT overlay is designed for it --
one shall overlay the status property from "disable" (defined in
the DTS of the shipping hardware -- the main board) to "okay" in
the overlay DTS where a connection actually happens.

And even in this case, it makes sense to me to disable both the
status and the driver of SPI/I2C for the main board. Otherwise,
memory could be wasted for standalone main board users at those
unused SPI/I2C buses -- and the memory might be larger than one
could expect depending on where drivers allocate data buffers.

> Point is that I don't _know_ how this is going to be used, so I'd
> rather keep it flexible.

Well, taking one step back, I am okay to follow your way if you
are really firm about it. Just please give me a more reply and
I will merge this change to that v5, dropping the is_visible().

Actually neither the is_visible nor inX_enable is that essential
for me. I am just trying to do what I feel right.

Thank you
Nicolin
Guenter Roeck Sept. 27, 2018, 4:05 p.m. UTC | #7
Hi Nicolin,

On Wed, Sep 26, 2018 at 02:55:06PM -0700, Nicolin Chen wrote:
> On Wed, Sep 26, 2018 at 01:44:55PM -0700, Guenter Roeck wrote:
> > On Wed, Sep 26, 2018 at 01:25:20PM -0700, Nicolin Chen wrote:
> > > Hello,
> > > 
> > > On Wed, Sep 26, 2018 at 12:58:17PM -0700, Guenter Roeck wrote:
> > > > On Wed, Sep 26, 2018 at 11:02:44AM -0700, Nicolin Chen wrote:
> > > > > On Wed, Sep 26, 2018 at 06:06:32AM -0700, Guenter Roeck wrote:
> > > > > > On 09/25/2018 11:42 PM, Nicolin Chen wrote:
> > > > > > > The inX_enable interface allows user space to enable or disable
> > > > > > > the corresponding channel. Meanwhile, according to hwmon ABI, a
> > > > > > > disabled channel/sensor should return -ENODATA as a read result.
> > > > > > > 
> > > > > > > However, there're configurable nodes sharing the same __show()
> > > > > > > functions. So this change also adds to check if the attribute is
> > > > > > > read-only to make sure it's not reading a configuration but the
> > > > > > > sensor data.
> > > > >  
> > > > > > One necessary high level change I don't see below: With this change,
> > > > > > we should no longer drop a channel entirely if it is disabled from
> > > > > > devicetree. All channels should be visible but report -ENODATA if
> > > > > > disabled. In other words, it should be possible for the 'enable' flag
> > > > > > to override settings in DT.
> > > > > 
> > > > > Hmm...I don't feel so convinced here. The status in DT binding isn't
> > > > > exactly a setting but a physical status: if a hardware design leaves
> > > > > a channel to be disconnected, I don't really see a point in enabling
> > > > > it in the runtime. Or maybe you can shed some light on it?
> > > > > 
> > > > 
> > > > You are making an assumption from your use case. It might as well be that
> > > > some or all channels are disabled in DT by default to conserve power,
> > > > not because they are disconnected.
> > > 
> > > I think I probably should update my DT binding somehow to say it
> > > explicitly that the property should be only used in cases of the
> > > physical disconnections, although I feel the current binding "no
> > > input source" already has the same meaning.
> > > 
> > > In my opinion, disabling channels in DT to save power isn't very
> > > plausible, because it sounds more likely a user decision, while,
> > > as we know, DT merely describes the hardware design.
> > > 
> > 
> > I try to avoid making such assumptions. All I know is that I'll have
> > to deal with the fallout if a single person wants to use the property
> > differently.
> 
> I can understand your point (or pain lol). But I believe in such
> a case, DT maintainers should reject such a DT change. Let's say
> if this kinda "default setting" in DT is allowable, other things
> such as having a default mode setting between polling or one-shot
> modes, and as default critical current settings would be able to
> put into DT. But we know that these would be rejected as a reason
> of "not being hardware design but a user decision".
> 
> > This is similar to disabling an entire subsystem in DT
> > because it isn't used in a specific system - say, a SPI or I2C bus
> > which has nothing connected on some shipping hardware, even though
> > the board has a connector for it. Your argument is that one shall
> > not use the status property do disable loading the driver, and that
> > one must not remove a set of properties for unused hardware either.
> > That doesn't sound very realistic to me.
> 
> SPI/I2C is a good example comparing to my case. And you do make
> a point. In that case, I think DT overlay is designed for it --
> one shall overlay the status property from "disable" (defined in
> the DTS of the shipping hardware -- the main board) to "okay" in
> the overlay DTS where a connection actually happens.
> 
> And even in this case, it makes sense to me to disable both the
> status and the driver of SPI/I2C for the main board. Otherwise,
> memory could be wasted for standalone main board users at those
> unused SPI/I2C buses -- and the memory might be larger than one
> could expect depending on where drivers allocate data buffers.
> 
> > Point is that I don't _know_ how this is going to be used, so I'd
> > rather keep it flexible.
> 
> Well, taking one step back, I am okay to follow your way if you
> are really firm about it. Just please give me a more reply and
> I will merge this change to that v5, dropping the is_visible().

Quite obviously we are not converging. Given that, I think it
would be better to shelve the new attributes for now and to wait
for an actual user who actually needs the new attributes.

Thanks,
Guenter
Nicolin Chen Sept. 27, 2018, 6:39 p.m. UTC | #8
Hello Guenter,

On Thu, Sep 27, 2018 at 09:05:09AM -0700, Guenter Roeck wrote:
> > > Point is that I don't _know_ how this is going to be used, so I'd
> > > rather keep it flexible.
> > 
> > Well, taking one step back, I am okay to follow your way if you
> > are really firm about it. Just please give me a more reply and
> > I will merge this change to that v5, dropping the is_visible().
> 
> Quite obviously we are not converging. Given that, I think it
> would be better to shelve the new attributes for now and to wait
> for an actual user who actually needs the new attributes.

I would rather remove the hiding code, if we have to sacrifice
one of them. We both agreed that in[123]_enable is more useful.
And actually we will still keep is_visible() for empty labels.
So even if one day we somehow feel safe to hide a disconnected
channel one day, it'd only need a couple of lines of changes.

I will squash this change into v6, and will send it after I fix
things that Rob pointed out at DT binding.

Thank you
Nicolin
Nicolin Chen Sept. 27, 2018, 10:26 p.m. UTC | #9
On Wed, Sep 26, 2018 at 06:06:32AM -0700, Guenter Roeck wrote:
> > +static inline bool ina3221_is_enable(struct ina3221_data *ina, int channel)
> 
> s/is_enable/is_enabled/, maybe ?

Fixing.

> > +	return (config & INA3221_CONFIG_CHx_EN(channel)) > 0;
> 
> The "> 0" is unnecessary. Conversion to bool is automatic. If you want to make
> it explicit, please use
> 
> 	return !!(config & INA3221_CONFIG_CHx_EN(channel));

Removing "> 0".

> It should not be necessary to re-read the value from the chip all the time.
> I would suggest to cache the value in the 'disabled' variable.

Regarding this part, I added a cache before sending this one. But
I realized if the chip got powered off and rebooted during system
suspend/resume, the cache would not reflect the actual status. As
I mentioned earlier, this was enlightened by your comments about
the BIOS. So I feel it'd be safer to read the register every time
at this point, until I add the suspend/resume feature by syncing
with regcache. What do you think about it?

> > +	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;
> > +
> Just use kstrtobool().

Fixing.

Thanks
Nicolin
Guenter Roeck Sept. 27, 2018, 10:52 p.m. UTC | #10
On Thu, Sep 27, 2018 at 03:26:14PM -0700, Nicolin Chen wrote:
> On Wed, Sep 26, 2018 at 06:06:32AM -0700, Guenter Roeck wrote:
> > > +static inline bool ina3221_is_enable(struct ina3221_data *ina, int channel)
> > 
> > s/is_enable/is_enabled/, maybe ?
> 
> Fixing.
> 
> > > +	return (config & INA3221_CONFIG_CHx_EN(channel)) > 0;
> > 
> > The "> 0" is unnecessary. Conversion to bool is automatic. If you want to make
> > it explicit, please use
> > 
> > 	return !!(config & INA3221_CONFIG_CHx_EN(channel));
> 
> Removing "> 0".
> 
> > It should not be necessary to re-read the value from the chip all the time.
> > I would suggest to cache the value in the 'disabled' variable.
> 
> Regarding this part, I added a cache before sending this one. But
> I realized if the chip got powered off and rebooted during system
> suspend/resume, the cache would not reflect the actual status. As
> I mentioned earlier, this was enlightened by your comments about
> the BIOS. So I feel it'd be safer to read the register every time
> at this point, until I add the suspend/resume feature by syncing
> with regcache. What do you think about it?
> 

The proper fix for this problem would be to add support for suspend /
resume to the driver. At resume time, all channels will have been
re-enabled if the chip was powered off, even if they were explicitly
disabled by devicetree (or via explicit configuration). This means
the driver just behaves badly across suspend/resume, period.
Displaying a raw value instead of a cached one doesn't solve that
problem. By using a cached value, at least the user would not notice
that the chip no longer does what it is supposed to be doing.

I guess we just have different priorities. If I think suspend/resume
is a problem for my use case, I would just go ahead and fix it.
I would not try to write code that doesn't fix the problem causing it,
much less argue for it.

Having said that, I didn't mention that part in my other reply,
meaning I'll accept the code as is.

Thanks,
Guenter
Nicolin Chen Sept. 27, 2018, 11:14 p.m. UTC | #11
On Thu, Sep 27, 2018 at 03:52:00PM -0700, Guenter Roeck wrote:

> The proper fix for this problem would be to add support for suspend /
> resume to the driver. At resume time, all channels will have been
> re-enabled if the chip was powered off, even if they were explicitly
> disabled by devicetree (or via explicit configuration). This means
> the driver just behaves badly across suspend/resume, period.
> Displaying a raw value instead of a cached one doesn't solve that
> problem. By using a cached value, at least the user would not notice
> that the chip no longer does what it is supposed to be doing.
>
> I guess we just have different priorities. If I think suspend/resume
> is a problem for my use case, I would just go ahead and fix it.
> I would not try to write code that doesn't fix the problem causing it,
> much less argue for it.

I agree.

> Having said that, I didn't mention that part in my other reply,
> meaning I'll accept the code as is.

Thanks for the generosity. But, since Rob hasn't acked yet, let
me write the patch for suspend and resume first, which shouldn't
take long.

Thanks
Nicolin
diff mbox series

Patch

diff --git a/Documentation/hwmon/ina3221 b/Documentation/hwmon/ina3221
index 6be64b553cd0..1aa32366d8aa 100644
--- a/Documentation/hwmon/ina3221
+++ b/Documentation/hwmon/ina3221
@@ -23,6 +23,7 @@  Sysfs entries
 
 in[123]_input           Bus voltage(mV) channels
 in[123]_label           Voltage channel labels
+in[123]_enable          Voltage channel enable controls
 curr[123]_input         Current(mA) measurement channels
 shunt[123]_resistor     Shunt resistance(uOhm) channels
 curr[123]_crit          Critical alert current(mA) setting, activates the
diff --git a/drivers/hwmon/ina3221.c b/drivers/hwmon/ina3221.c
index 5890a2da3bfe..1be2d062a19e 100644
--- a/drivers/hwmon/ina3221.c
+++ b/drivers/hwmon/ina3221.c
@@ -78,6 +78,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,
@@ -113,6 +116,19 @@  struct ina3221_data {
 	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;
+}
+
 static ssize_t ina3221_show_label(struct device *dev,
 				  struct device_attribute *attr, char *buf)
 {
@@ -124,6 +140,45 @@  static ssize_t ina3221_show_label(struct device *dev,
 	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;
+
+	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)
 {
@@ -146,8 +201,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;
@@ -164,8 +224,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;
@@ -201,8 +266,13 @@  static ssize_t ina3221_show_current(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 current_ma, ret;
 
+	/* No data for read-only attribute if channel is disabled */
+	if (!attr->store && !ina3221_is_enable(ina, channel))
+		return -ENODATA;
+
 	ret = __ina3221_show_current(ina, reg, &current_ma);
 	if (ret)
 		return ret;
@@ -318,6 +388,10 @@  static ssize_t ina3221_show_power(struct device *dev,
 	int val, current_ma, voltage_mv, ret;
 	s64 power_uw;
 
+	/* No data for read-only attribute if channel is disabled */
+	if (!attr->store && !ina3221_is_enable(ina, channel))
+		return -ENODATA;
+
 	/* Read bus voltage */
 	ret = ina3221_read_value(ina, reg_bus, &val);
 	if (ret)
@@ -377,6 +451,14 @@  static SENSOR_DEVICE_ATTR(in2_label, 0444,
 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);
@@ -460,6 +542,7 @@  static SENSOR_DEVICE_ATTR(power3_crit, 0644,
 static struct attribute *ina3221_attrs[] = {
 	/* 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,
@@ -473,6 +556,7 @@  static struct attribute *ina3221_attrs[] = {
 
 	/* 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,
@@ -486,6 +570,7 @@  static struct attribute *ina3221_attrs[] = {
 
 	/* 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,