Hello all,
We need to support a hardware that can measure current and voltage on
the same differential analog input, similar to a multimeter. The mode
of measurement is controlled by a GPIO switch and goes to different
ADC inputs depending on the mode. If the switch is enabled, a current
loop with a shunt is enabled for current measurement; otherwise, voltage
is measured. From the software point of view, we are considering using
the iio-rescale driver as a consumer of an ADC IIO parent device. One
of the problems is that we need to change the mode of measurement at
runtime, but we are trying to avoid using some userspace "hack". The
other is that for a minimal solution to enable the mode from boot, we
can use a gpio-hog and control it with overlays. However,
still would be better that this was done by the kernel. Do you know
or have some guidance on how to properly support this in the kernel?
For the in kernel gpio solution, this is a draft of DT we are thinking:
current-sense {
compatible = "current-sense-shunt";
io-channels = <&adc 0>;
gpio = <&main_gpio0 29 GPIO_ACTIVE_HIGH>;
shunt-resistor-micro-ohms = <3300000>;
};
voltage-sense {
compatible = "voltage-divider";
io-channels = <&adc 1>;
gpio = <&main_gpio0 29 GPIO_ACTIVE_LOW>;
output-ohms = <22>;
full-ohms = <222>;
};
Regards,
João Paulo Gonçalves
[also Cc: IIO and devicetree maintainers]
On Wed, May 01, 2024 at 08:38:53PM -0300, João Paulo Gonçalves wrote:
> Hello all,
>
> We need to support a hardware that can measure current and voltage on
> the same differential analog input, similar to a multimeter. The mode
> of measurement is controlled by a GPIO switch and goes to different
> ADC inputs depending on the mode. If the switch is enabled, a current
> loop with a shunt is enabled for current measurement; otherwise, voltage
> is measured. From the software point of view, we are considering using
> the iio-rescale driver as a consumer of an ADC IIO parent device. One
> of the problems is that we need to change the mode of measurement at
> runtime, but we are trying to avoid using some userspace "hack". The
> other is that for a minimal solution to enable the mode from boot, we
> can use a gpio-hog and control it with overlays. However,
> still would be better that this was done by the kernel. Do you know
> or have some guidance on how to properly support this in the kernel?
>
> For the in kernel gpio solution, this is a draft of DT we are thinking:
>
> current-sense {
> compatible = "current-sense-shunt";
> io-channels = <&adc 0>;
> gpio = <&main_gpio0 29 GPIO_ACTIVE_HIGH>;
> shunt-resistor-micro-ohms = <3300000>;
> };
>
> voltage-sense {
> compatible = "voltage-divider";
> io-channels = <&adc 1>;
> gpio = <&main_gpio0 29 GPIO_ACTIVE_LOW>;
> output-ohms = <22>;
> full-ohms = <222>;
> };
What are these ohms and where do you get them?
Confused...
--
An old man doll... just what I always wanted! - Clara
On Thu, May 02, 2024 at 07:31:15AM +0700, Bagas Sanjaya wrote:
> [also Cc: IIO and devicetree maintainers]
> What are these ohms and where do you get them?
>
> Confused...
Ignore them. They are not real values. I got them from the
bidings example of iio-rescale devices. The DT draft is an
example, not the final/correct form.
Regards,
João Paulo Gonçalves
On 5/2/24 07:42, João Paulo Silva Gonçalves wrote:
> On Thu, May 02, 2024 at 07:31:15AM +0700, Bagas Sanjaya wrote:
>> [also Cc: IIO and devicetree maintainers]
>> What are these ohms and where do you get them?
>>
>> Confused...
>
> Ignore them. They are not real values. I got them from the
> bidings example of iio-rescale devices. The DT draft is an
> example, not the final/correct form.
>
OK, thanks!
--
An old man doll... just what I always wanted! - Clara
Hi!
2024-05-02 at 14:36, Jonathan Cameron wrote:
> On Wed, 1 May 2024 20:38:53 -0300
> João Paulo Gonçalves <[email protected]> wrote:
>
>> Hello all,
>>
>> We need to support a hardware that can measure current and voltage on
>> the same differential analog input, similar to a multimeter. The mode
>> of measurement is controlled by a GPIO switch and goes to different
>> ADC inputs depending on the mode. If the switch is enabled, a current
>> loop with a shunt is enabled for current measurement; otherwise, voltage
>> is measured. From the software point of view, we are considering using
>> the iio-rescale driver as a consumer of an ADC IIO parent device. One
>> of the problems is that we need to change the mode of measurement at
>> runtime, but we are trying to avoid using some userspace "hack". The
>> other is that for a minimal solution to enable the mode from boot, we
>> can use a gpio-hog and control it with overlays. However,
>> still would be better that this was done by the kernel. Do you know
>> or have some guidance on how to properly support this in the kernel?
>>
>> For the in kernel gpio solution, this is a draft of DT we are thinking:
>>
>> current-sense {
>> compatible = "current-sense-shunt";
>> io-channels = <&adc 0>;
>> gpio = <&main_gpio0 29 GPIO_ACTIVE_HIGH>;
>> shunt-resistor-micro-ohms = <3300000>;
>> };
>>
>> voltage-sense {
>> compatible = "voltage-divider";
>> io-channels = <&adc 1>;
>> gpio = <&main_gpio0 29 GPIO_ACTIVE_LOW>;
>> output-ohms = <22>;
>> full-ohms = <222>;
>> };
>>
>> Regards,
>> João Paulo Gonçalves
>>
> +CC Peter Rosin who wrote all the relevant parts you need I think.
>>
>
> Superficially sounds like you want a mixture of appropriate analog front ends
> and a Mux. I haven't tried the combination but it should be possible to do
> something like this with
>
> An IIO mux via this binding
> https://elixir.bootlin.com/linux/v6.9-rc6/source/Documentation/devicetree/bindings/iio/multiplexer/io-channel-mux.yaml
> (that includes a gpio-mux example).
>
> Consumed in turn by a pair of AFE devices.
>
> Then you should be able to just read from which ever of the AFE device you want.
> A sysfs read from
> /sys/bus/iio/devices/iio\:deviceA/in_voltage_raw
> will switch the mux to appropriate place then request the
> voltage from the iio-mux, which in turn requests it from the ADC IIO driver.
>
> /sys/bus/iio/devices/iio\:deviceB/in_current_raw
> switches the mux the other way and otherwise the flow as above.
Since you appear to need to change both the gpio pin and the io-channel, the
mux isn't a perfect fit. The closest you can get with the current code is to
create a gpio mux, I think. You would then use that mux twice to fan out both
io-channels, but only expose the "left leg" on the first fan-out and only the
"right leg" on the other. Something like this (untested, probably riddled with
errors, use salt etc etc):
rcs: raw-current-sense {
compatible = "current-sense-shunt";
io-channels = <&adc 0>;
io-channel-name = "raw-current";
#io-channel-cells = <1>;
shunt-resistor-micro-ohms = <3300000>;
};
rvs: raw-voltage-sense {
compatible = "voltage-divider";
io-channels = <&adc 1>;
io-channel-name = "raw-voltage";
#io-channel-cells = <1>;
output-ohms = <22>;
full-ohms = <222>;
};
mux: gpio-mux {
compatible = "gpio-mux";
#mux-control-cells = <0>;
gpios-mux = <&main_gpio0 29 GPIO_ACTIVE_HIGH>;
};
current-sense {
compatible = "io-channel-mux";
io-channels = <&rcs 0>;
io-channel-names = "parent";
mux-controls = <&mux>;
channels = "current", "";
};
voltage-sense {
compatible = "io-channel-mux";
io-channels = <&rvs 0>;
io-channel-names = "parent";
mux-controls = <&mux>;
channels = "", "voltage";
};
What the mux solves is exclusion, so that the gpio pin is locked while
measurement is made on either current-sense or voltage-sense.
However, the channels from the raw-{current,voltage}-sense nodes are exposed
to user space, and it will be possible to make "raw" measurements without
regard to how the gpio pin is set. That will of course not yield the desired
results, but is also a user error and might not be a big problem?
Cheers,
Peter
2024-05-02 at 15:49, Peter Rosin wrote:
> Since you appear to need to change both the gpio pin and the io-channel, the
> mux isn't a perfect fit. The closest you can get with the current code is to
> create a gpio mux, I think. You would then use that mux twice to fan out both
> io-channels, but only expose the "left leg" on the first fan-out and only the
> "right leg" on the other. Something like this (untested, probably riddled with
> errors, use salt etc etc):
>
> rcs: raw-current-sense {
> compatible = "current-sense-shunt";
> io-channels = <&adc 0>;
> io-channel-name = "raw-current";
> #io-channel-cells = <1>;
>
> shunt-resistor-micro-ohms = <3300000>;
> };
>
> rvs: raw-voltage-sense {
> compatible = "voltage-divider";
> io-channels = <&adc 1>;
> io-channel-name = "raw-voltage";
> #io-channel-cells = <1>;
>
> output-ohms = <22>;
> full-ohms = <222>;
> };
>
> mux: gpio-mux {
> compatible = "gpio-mux";
> #mux-control-cells = <0>;
>
> gpios-mux = <&main_gpio0 29 GPIO_ACTIVE_HIGH>;
> };
>
> current-sense {
> compatible = "io-channel-mux";
> io-channels = <&rcs 0>;
> io-channel-names = "parent";
>
> mux-controls = <&mux>;
>
> channels = "current", "";
> };
>
> voltage-sense {
> compatible = "io-channel-mux";
> io-channels = <&rvs 0>;
> io-channel-names = "parent";
>
> mux-controls = <&mux>;
>
> channels = "", "voltage";
> };
>
> What the mux solves is exclusion, so that the gpio pin is locked while
> measurement is made on either current-sense or voltage-sense.
>
> However, the channels from the raw-{current,voltage}-sense nodes are exposed
> to user space, and it will be possible to make "raw" measurements without
> regard to how the gpio pin is set. That will of course not yield the desired
> results, but is also a user error and might not be a big problem?
I just realized that it's also possible to do this "the other way around". Maybe
that makes more sense?
Cheers,
Peter
mux: gpio-mux {
compatible = "gpio-mux";
#mux-control-cells = <0>;
gpios-mux = <&main_gpio0 29 GPIO_ACTIVE_HIGH>;
};
rcs: raw-current-sense {
compatible = "io-channel-mux";
io-channels = <&adc 0>;
io-channel-names = "parent";
#io-channel-cells = <1>;
mux-controls = <&mux>;
channels = "raw-current", "";
};
rvs: raw-voltage-sense {
compatible = "io-channel-mux";
io-channels = <&adc 1>;
io-channel-names = "parent";
#io-channel-cells = <1>;
mux-controls = <&mux>;
channels = "", "raw-voltage";
};
current-sense {
compatible = "current-sense-shunt";
io-channels = <&rcs 0>;
io-channel-name = "current";
shunt-resistor-micro-ohms = <3300000>;
};
voltage-sense {
compatible = "voltage-divider";
io-channels = <&rvs 1>;
io-channel-name = "voltage";
output-ohms = <22>;
full-ohms = <222>;
};
Cheers,
Peter
On Thu, May 02, 2024 at 01:36:23PM +0100, Jonathan Cameron wrote:
> Superficially sounds like you want a mixture of appropriate analog front ends
> and a Mux. I haven't tried the combination but it should be possible to do
> something like this with
>
> An IIO mux via this binding
> https://elixir.bootlin.com/linux/v6.9-rc6/source/Documentation/devicetree/bindings/iio/multiplexer/io-channel-mux.yaml
> (that includes a gpio-mux example).
>
> Consumed in turn by a pair of AFE devices.
>
> Then you should be able to just read from which ever of the AFE device you want.
> A sysfs read from
> /sys/bus/iio/devices/iio\:deviceA/in_voltage_raw
> will switch the mux to appropriate place then request the
> voltage from the iio-mux, which in turn requests it from the ADC IIO driver.
>
> /sys/bus/iio/devices/iio\:deviceB/in_current_raw
> switches the mux the other way and otherwise the flow as above.
>
> Jonathan
>
On Thu, May 02, 2024 at 03:49:03PM +0200, Peter Rosin wrote:
> I just realized that it's also possible to do this "the other way around". Maybe
> that makes more sense?
>
> Cheers,
> Peter
>
> mux: gpio-mux {
> compatible = "gpio-mux";
> #mux-control-cells = <0>;
>
> gpios-mux = <&main_gpio0 29 GPIO_ACTIVE_HIGH>;
> };
>
> rcs: raw-current-sense {
> compatible = "io-channel-mux";
> io-channels = <&adc 0>;
> io-channel-names = "parent";
> #io-channel-cells = <1>;
>
> mux-controls = <&mux>;
>
> channels = "raw-current", "";
> };
>
> rvs: raw-voltage-sense {
> compatible = "io-channel-mux";
> io-channels = <&adc 1>;
> io-channel-names = "parent";
> #io-channel-cells = <1>;
>
> mux-controls = <&mux>;
>
> channels = "", "raw-voltage";
> };
>
> current-sense {
> compatible = "current-sense-shunt";
> io-channels = <&rcs 0>;
> io-channel-name = "current";
>
> shunt-resistor-micro-ohms = <3300000>;
> };
>
> voltage-sense {
> compatible = "voltage-divider";
> io-channels = <&rvs 1>;
> io-channel-name = "voltage";
>
> output-ohms = <22>;
> full-ohms = <222>;
> };
>
> Cheers,
> Peter
A lot of good information. I didn't know about the iio-mux. It might
solve our problem, and I will do some testing with it.
Thanks a lot for the help!
Regards,
João Paulo Gonçalves
2024-05-02 at 17:09, Jonathan Cameron wrote:
> On Thu, 2 May 2024 16:05:45 +0200
> Peter Rosin <[email protected]> wrote:
>
>> 2024-05-02 at 15:49, Peter Rosin wrote:
>>> Since you appear to need to change both the gpio pin and the io-channel, the
>>> mux isn't a perfect fit. The closest you can get with the current code is to
>>> create a gpio mux, I think. You would then use that mux twice to fan out both
>>> io-channels, but only expose the "left leg" on the first fan-out and only the
>>> "right leg" on the other. Something like this (untested, probably riddled with
>>> errors, use salt etc etc):
>>>
>>> rcs: raw-current-sense {
>>> compatible = "current-sense-shunt";
>>> io-channels = <&adc 0>;
>>> io-channel-name = "raw-current";
>>> #io-channel-cells = <1>;
>>>
>>> shunt-resistor-micro-ohms = <3300000>;
>>> };
>>>
>>> rvs: raw-voltage-sense {
>>> compatible = "voltage-divider";
>>> io-channels = <&adc 1>;
>>> io-channel-name = "raw-voltage";
>>> #io-channel-cells = <1>;
>>>
>>> output-ohms = <22>;
>>> full-ohms = <222>;
>>> };
>>>
>>> mux: gpio-mux {
>>> compatible = "gpio-mux";
>>> #mux-control-cells = <0>;
>>>
>>> gpios-mux = <&main_gpio0 29 GPIO_ACTIVE_HIGH>;
>>> };
>>>
>>> current-sense {
>>> compatible = "io-channel-mux";
>>> io-channels = <&rcs 0>;
>>> io-channel-names = "parent";
>>>
>>> mux-controls = <&mux>;
>>>
>>> channels = "current", "";
>>> };
>>>
>>> voltage-sense {
>>> compatible = "io-channel-mux";
>>> io-channels = <&rvs 0>;
>>> io-channel-names = "parent";
>>>
>>> mux-controls = <&mux>;
>>>
>>> channels = "", "voltage";
>>> };
>>>
>>> What the mux solves is exclusion, so that the gpio pin is locked while
>>> measurement is made on either current-sense or voltage-sense.
>>>
>>> However, the channels from the raw-{current,voltage}-sense nodes are exposed
>>> to user space, and it will be possible to make "raw" measurements without
>>> regard to how the gpio pin is set. That will of course not yield the desired
>>> results, but is also a user error and might not be a big problem?
>>
>> I just realized that it's also possible to do this "the other way around". Maybe
>> that makes more sense?
> Ah, I'd failed to realize that this is about routing a single wire
> through two different analog circuits that end on 'different' ADC inputs.
>
> Pictures would help me out btw! Everyone loves ascii art.
Certainly! :-)
> Anyhow, I 'think' what you have here should work.
Also, on ordinary multimeters it is quite easy to blow the fuse for the current
loop. Assuming that measuring current is more sensitive, the following addition
might be in order?
@mux {
idle-state = 1;
};
Cheers,
Peter