Page 1 of 2

Overlay for on/off push-button controller.

Posted: Mon Jun 22, 2015 2:55 am
by sparkyPi
Hello everyone,

I am trying to write a device tree overlay (using GPIO device tree binding for "power off", see here) for a push-button on/off controller (Linear Technology LTC2951-1). I attached an image showing the schematic of LTC2951 and Raspberry Pi GPIOs. Unfortunately I've not been able to get it to work as I expect. Here is a little info about the setup to explain my process and where I'm at.

The LTC2951 has an active-low interrupt output (/INT) which it drives low when a normally-open push-button is pressed. When the Raspberry Pi detects /INT going low, the system starts its shutdown procedure. I accomplish this step with the following Python script (which is started at boot in /etc/rc.local):

Code: Select all

import os
import time
import RPi.GPIO as GPIO

# This script runs at boot and listens on /INT for shutdown request by LTC2951.
# When /INT = 0 is detected by interrupt the system initiates shutdown and will end in 'halt' state.
# Define input/output pins connected to LTC2951 pushbutton controller
nINT = 20                   # LTC2951 /INT output connected to RPi input

# Configure pins used by on/off pushbutton controller
GPIO.setmode(GPIO.BCM)          # Use Broadcom (BCM) pin numbering
GPIO.setup(nINT, GPIO.IN)       # Input : listen for /INT from on/off controller

# Function to run when on/off button press detected
def isr_shutdown(self):
    print("Calibration Box - LTC2951 shutdown request received")
    os.system("sudo shutdown -h now")   # Software system shutdown (commence immediately)

# Configure interrupt detection and assign callback function
GPIO.add_event_detect(nINT, GPIO.FALLING, callback=isr_shutdown, bouncetime=2000)

while(True):
    time.sleep(1)
This part works perfectly and the system ends in the "halt" state. However, the system is not completely shutdown yet --- the voltage regulators on my board are still powered driving LEDs etc. What is needed is the power to be cut completely by switching the regulators off (equivalent to unplugging the power source from Raspberry Pi).

To do this the active-low /KILL input of LTC2951 needs to be driven low. I am trying to accomplish it using the device tree GPIO binding that seems specifically for this purpose.

As shown in the schematic I have GPIO 26 of the Raspberry Pi connected to the /KILL input, but the RPi is not driving this pin low when the system reaches 'halt'. Following is the device tree configuration I have followed to try and get it to work.

First, I used information from here and here to edit dt-blob.dts and compile my own /boot/dt-blob.bin. Here is relevant snippet of dt-blob.dts which shows configuring GPIO 26 as an active-low output defaulting to inactive state at boot:

Code: Select all

<snip>
      pins_2b2 { // Pi 2 Model B rev 1.1
         pin_config {
            [email protected] {
               polarity = "active_high";
               termination = "pull_down";
               startup_state = "inactive";
               function = "input";
            }; // pin
         
            [email protected] { function = "output"; termination = "no_pulling"; polarity = "active_low"; startup_state = "inactive"; };  // Power off signal
            // Continues as original file...
            [email protected] { function = "uart0";  termination = "no_pulling"; drive_strength_mA = < 8 >; }; // TX uart0
<snip>
I confirmed this part works by testing with simple LED and note that the LED was on or off as expected. Since /KILL is active low, the above code will cause the pin to be driven high during boot, which is necessary otherwise power would be cut immediately.

Now I used information about device tree overlay from here to add the power-off function which should result in GPIO 26 going low once the system reaches 'halt', and consequently cutting power. Here is what I have written for the DT overlay:

Code: Select all

/dts-v1/;
/plugin/;

/ {
    compatible = "brcm,bcm2709";

    [email protected] {
        target = <&gpio>;
        __overlay__ {
            power_ctrl: power_ctrl {
                compatible = "gpio-poweroff";
                gpios = <&gpio 26 0>;
            };
        };
    };

};
I compiled the overlay and copied it to /boot/overlays/ and load it in config.txt by adding dtoverlay=mypowerswitch I confirmed the overlay is loaded and tested similar things with LEDs. Also, I can see the directory /proc/device-tree/soc/gpio/power_ctrl and files there.

Despite all this "power off" is not working --- specifically GPIO 26 does not go low when the system reaches 'halt', therefore the power stays on after the system is shutdown.

Compared to other overlay examples, mine looks rather simple. I wonder if I am missing something, like setting the GPIO to use in one fragment and then configure the pin as a "power controller" in a second fragment...similar to this example.

Or, is there a difference in how the system is shutdown? For example, I call "shutdown -h now" from my Python script, but what about "poweroff", "halt", or "init 0". Are all these expected to equivalently result in the pm_power_off handler being called?

Thanks for any and all help, folks. The device tree stuff is fascinating for configuring custom hardware, but seems tricky to get the overlay stuff right. :)

Cheers,
sparkyPi

Re: Overlay for on/off push-button controller.

Posted: Mon Jun 22, 2015 8:56 am
by PhilE
I think your main problem is simply that the standard Pi configurations don't include the gpio-poweroff driver. It must be enabled using the POWER_RESET and POWER_RESET_GPIO flags. If you create an issue in the raspberrypi/linux github repo requesting these additions (with a brief explanation) I think it is likely to be approved.

Re: Overlay for on/off push-button controller.

Posted: Mon Jun 22, 2015 9:09 am
by PhilE
Note that this will replace the standard RPi pm_power_off handler (bcm2708_power_off or bcm2709_power_off) that gets the Pi into as low a power-state as possible. But, since the replacement is going to result in actually switching off the power, and since it is only enabled by a DT overlay, I don't think that is a problem.

Re: Overlay for on/off push-button controller.

Posted: Mon Jun 22, 2015 4:48 pm
by sparkyPi
Thank you, PhilE, for your prompt reply and giving me some direction on this. I've never submit an Issue on github before; I'll get right on it. I would love to see this feature added :D

Edit: And here it is on github.

Excellent to see already that it will be included in next update -- can't wait! :D

Re: Overlay for on/off push-button controller.

Posted: Wed Jun 24, 2015 7:28 pm
by PhilE
I think I see the problem. The overlay fragment is targeting the gpio node, which may prevent the platform device from being created. Instead, place the fragment in the root like this:

Code: Select all

        target-path = "/";
        __overlay__ {
            power_ctrl: power_ctrl {
                compatible = "gpio-poweroff";
                gpios = <&gpio 26 0>;
            };
        };
I've also deleted the power_ctrl label because it served no purpose - nothing is referring to it (or is likely to). With that change, you should find that the gpio becomes an output as expected.

An oddity of the way gpios and the pinctrl subsystem work is that pinctrl assigments and gpio assignments are allowed to coexist on a single pin. Personally I think it is a bad feature, but it is supposedly to allow for "unused" pins in defined groups to be used for gpio. As a result, if you want to ensure that nothing else uses this pin (and you really should since it will cause your Pi to power-off) then you also need to create a pinctrl group for it using a second fragment:

Code: Select all

	[email protected] {
		target = <&gpio>;
		__overlay__ {
			power_ctrl_pins:power_ctrl_pins {
				brcm,pins = <26>;
				brcm,function = <0>; // in (initially)
			};
		};
	};
Finally, if you want to make the choice of gpio a parameter, you can add another section in before the final closing brace:

Code: Select all

	__overrides__ {
		gpiopin =       <&power_ctrl>,"gpios:4",
				<&power_ctrl_pins>,"brcm,pins:0";
	};
};
That should do the trick.

Re: Overlay for on/off push-button controller.

Posted: Thu Jun 25, 2015 2:57 am
by sparkyPi
Thanks for your input PhilE, and pointing over here from github. I have tried with just the essentials in the overlay:

Code: Select all

/dts-v1/;
/plugin/;

/ {
    compatible = "brcm,bcm2709";
    [email protected] {
        target-path = "/";
        __overlay__ {
            power_ctrl: power_ctrl {
                compatible = "gpio-poweroff";
                gpios = <&gpio 26 0>;
            };
        };
    };

};
Unfortunately, it's still not working -- the GPIO 26 remains high once the RPi reaches halt. The pin seems to be configuring correctly as an output when dt-blob.bin is read because I notice a jump from in voltage (~2.8V from my pull-up to 3.3V when driving high by GPIO 26 as output) on the oscilloscope.
PhilE wrote:I've also deleted the power_ctrl label because it served no purpose - nothing is referring to it (or is likely to). With that change, you should find that the gpio becomes an output as expected.
I don't see which label you removed... Thinking you might have meant to remove "power_ctrl" after the colon in the code above, I tried it but the code wouldn't compile.

I'm not sure where the problem is, but I can look over other .dts overlays for ideas...

Re: Overlay for on/off push-button controller.

Posted: Thu Jun 25, 2015 11:23 am
by PhilE
It was the power_ctrl label I removed, but then I re-added it later to support the parameter.

@notro was correct about existing pm_power_down handler causing the module to fail to install. This is the intended behaviour, but not what we want. My solution is to patch the module to add another DT property ("force") that causes it to overwrite the existing handler, and to set that property in the overlay.

Also, since you are driving an active-low input we need to invert the sense of signal. I've enabled this with a DT parameter since some people might want an active-high signal, and using it in active-low mode requires a custom dt-blob.bin to prevent it from powering off while booting (the 512ms hold-off is nowhere near long enough).

I have observed a problem though - rebooting (sudo reboot, or sudo shutdown -r now) will cause the pin level to drop, which will power-down your Pi. As a result you may prefer to invert the trigger signal externally.

I've created a gist containing my patch, in case you want to try it: https://gist.github.com/pelwell/e4c5662c331fcae1e707

Re: Overlay for on/off push-button controller.

Posted: Thu Jun 25, 2015 7:11 pm
by sparkyPi
@PhilE, thanks for drilling down on the issue and creating a patch with all the necessary Makefile/README and overlay.dts That's really a lot more support than I ever expected. You guys are a great team. :D
PhilE wrote:...using it in active-low mode requires a custom dt-blob.bin to prevent it from powering off while booting (the 512ms hold-off is nowhere near long enough).
Yes; I specified active-low polarity and inactive (high) in my custom dt-blob.bin for GPIO 26 but still had to put a hardware pull-up on /KILL otherwise the power would be cut during boot. Your solution to invert the signal externally is a good one -- I can avoid the pull-up and custom dt-blob.bin. I will test it in hardware.
PhilE wrote:I've created a gist containing my patch, in case you want to try it: https://gist.github.com/pelwell/e4c5662c331fcae1e707
I would love to test it, but I'm not sure how to apply the patch -- seems I will have to download and build the entire source? If there's an alternate way please let me know and I'll give it a shot. In any case, I hope these changes can make it through to the next 4.0.y kernel update.

Re: Overlay for on/off push-button controller.

Posted: Thu Jun 25, 2015 7:17 pm
by PhilE
I'm happy that the patch won't break anything else, and my limited testing suggests it actually works, so I'll push it tomorrow.

Re: Overlay for on/off push-button controller.

Posted: Fri Jun 26, 2015 6:57 am
by paulv
Very clever this little chip. Good find Sparky!
The datasheet shows a number of applicable applications for the Pi. http://cds.linear.com/docs/en/datasheet/295112fb.pdf

I cannot yet grasp what is happening with what you are all doing with the device tree changes at the moment. I get the principle, but it goes a little over my head right now.

However, is it not possible to somehow use the TXD pad for the /Kill signal? It goes low after the shutdown procedure has ended. The trick may be to ignore or avoid the boot activity on this pad.

Re: Overlay for on/off push-button controller.

Posted: Fri Jun 26, 2015 8:27 pm
by sparkyPi
@paulv, I agree these controllers are very useful in embedded systems - a very elegant way to start and shutdown a system.

With on/off controllers like this whenever /KILL is asserted (low in this case) the ENable output (which controls voltage regulators on my custom board which Raspberry Pi plugs into) goes low, cutting off power immediately -- in general you don't want this pin to be multiplexed with another function. Regardless of which pin is used, I think it best to have direct control of the logic level of the pin and not rely on it "floating down".

Re: Overlay for on/off push-button controller.

Posted: Fri Jun 26, 2015 8:29 pm
by sparkyPi
@PhilE, thanks so much for your help. I tested your patch in the last update and it works perfectly! Great to see the gpio-poweroff overlay as an official part of the release. This issue is solved :D Cheers!

Re: Overlay for on/off push-button controller.

Posted: Sat Jun 27, 2015 7:20 am
by paulv
Hi Sparky,

Glad to see you have resolved this together with PhilE. Very interesting collaboration!

This is a significant step to add better power control to the Pi. I also use UPS based power controllers to allow the Pi in embedded (server) applications and have documented several on this forum. One How-To is documented here : viewtopic.php?f=37&t=102015

I would like to try and work this new feature out myself, and do a write-up for non-power users, but will need your help for the device-tree part of it. I can't seem to get it to work copying your steps. Do you mind documenting the steps you took to get the power-down GPIO pad working?

While trying to work out your power down method, I actually discovered a way to accomplish the same(?) by just (and only) changing the dt-blob file. I would have never gotten this idea if it weren't for the fiddling with your post. Here is the snippet from my dt-blob version:

Code: Select all

      pins_rev2 {
         pin_config {
            [email protected] {
               polarity = "active_high";
               termination = "pull_down";
               startup_state = "inactive";
               function = "input";
            }; // pin
// using GPIO-25 as the power-down signal pad
            [email protected] { function = "output"; termination = "pull_down"; polarity = "active_low"; startup_state = "inactive"; };
//
            [email protected]  { function = "i2c0";   termination = "pull_up"; }; // I2C 0 SDA
etc...
So what I'm doing here is to set the termination to pull_low, rather than no_pull or tri-state which is what you have. Setting the termination to pull_low will actually force the pad low when the SOC is halted. (note that in previous kernel versions, the pads where tri-state as default.) The pad will be high because of the output definition. The best way to look at the pull activity is to use two high value (I use 1M) resistors. One from 3V3 to the pad, and one from the pad to GND. Connect your scope or DMM between GND and the pad.

Here are my steps:
download the dt-blob.dts file from the RaspberryPi.org server.
copy the file to my-blob.dts
make the above change to whatever port you want to use. Make sure you use the correct section in the blob file, depending on your Pi model.
compile the file:
sudo dtc -I dts -O dtb -o /boot/dt-blob.bin my-blob.dts
And reboot.

Within 800 milli-seconds after power has been applied to the Pi or is rebooted, the above configuration pulls the pad to a high value.
High-Boot.JPG
High-Boot.JPG (38.04 KiB) Viewed 13721 times
Trace A = 3V3 supply, Trace B is GPIO-25.
When the Pi is halted (just before the LED starts to flash 10x), the pad goes low.
High-Halt.JPG
High-Halt.JPG (37.97 KiB) Viewed 13721 times
Trace A = TXD (falls at Halt), Trace B is GPIO 25.
Next is a shot of a reboot.
IMG_2140.JPG
IMG_2140.JPG (22.42 KiB) Viewed 13697 times
Trace A is GPIO-25 and B is the TXD pin as a reference.
The GPIO is only undefined for 600mSec, whatever hardware you connect must account for this period and not switch the power off too soon.

Maybe this helps too,
paulv

Re: Overlay for on/off push-button controller.

Posted: Sat Jun 27, 2015 11:23 am
by paulv
I wish we could add a few more attachments...

If you drive a Buck/Boost DC-DC convertor directly, you may have to make the pad active low (as above), or active high with:

Code: Select all

[email protected] { function = "output"; termination = "pull_up"; polarity = "active_high"; startup_state = "inactive"; };
Low-Boot.JPG
Low-Boot.JPG (38.77 KiB) Viewed 13721 times
Trace A is 3V3, Trace B is GPIO-25.
Low-Halt.JPG
Low-Halt.JPG (39.19 KiB) Viewed 13721 times
Trace A is TXD, Trace B is GPIO-25
This depends on the DC-DC convertor you're using. Some are active low, some are active high. (As an example, the XL6009 is active high, the LM2596 is active low )

Enjoy!

Re: Overlay for on/off push-button controller.

Posted: Mon Jun 29, 2015 1:51 am
by sparkyPi
paulv wrote: Glad to see you have resolved this together with PhilE. Very interesting collaboration!

This is a significant step to add better power control to the Pi. I also use UPS based power controllers to allow the Pi in embedded (server) applications and have documented several on this forum. One How-To is documented here : viewtopic.php?f=37&t=102015

I would like to try and work this new feature out myself, and do a write-up for non-power users, but will need your help for the device-tree part of it. I can't seem to get it to work copying your steps. Do you mind documenting the steps you took to get the power-down GPIO pad working?
Thanks for your comments paulv -- I'm happy to know people find the information useful. I had a look at your UPS based power controller -- awesome thread! So much detail with many good ideas and discussion presented :) The power-loss detection to enable graceful shutdown is an excellent inclusion. From your post it seems you follow what is happening; here I will add some notes which I hope are helpful.

Regarding the shutdown feature and how to get it working, it is first necessary to do "sudo rpi-update" to get the current (at time of writing) 4.0.6 kernel firmware. This kernel (or above) is required as it includes the patches developed by PhilE to allow the device tree overlay to work (by overriding the existing pm_power_down handler), and the gpio-poweroff-overlay itself. What this overlay does is configure one pin as an output that transitions to an active state (either low or high) when the system reaches Halt. In my case LTC2951 uses that input signal to turn the power circuit on/off.

Because the gpio-poweroff overlay is provided already compiled (/boot/overlays/gpio-poweroff-overlay.dtb), you don't need to compile it from its .dts source.

Once the kernel is up to date add the following to the end of /boot/config.txt:

Code: Select all

dtoverlay=gpio-poweroff
and reboot.

This will load the gpio-poweroff overlay at boot time which makes use of GPIO 26 (default assignment = pin 37 of the 40-way header) as the "power control" signal (/KILL input of LTC2951). The GPIO to use can be changed from GPIO 26 using an optional argument to the dtoverlay= line. Furthermore, you can specify if the line is active high or low. See /boot/overlays/README for optional arguments to dtoverlay=gpio-poweroff

The LTC2951 is a basic controller -- other LTC295x variants have voltage monitoring/power fail indication, auto power-on, etc. functions. Some of these might better suit UPS type applications. Also, many of the controllers are available with both active high EN (LTC2951-1), and active low EN (LTC2951-2) outputs. Still there are other controllers that have multi-voltage monitoring capability...

As you show in oscilloscope images the GPIOs may not start in the desired state (high or low) at boot, or reboot, and it can take few hundred milliseconds for custom pin changes to be loaded from dt-blob.bin. The controllers have a "blanking time" during which they ignore the /KILL input after the push-button is pressed, but even the 512msec of LTC2951 is not long enough. I had to connect a hardware pull-up on the /KILL pin to ensure it was in inactive state (otherwise the controller would cut the power immediately).

The main benefit of the overlay is that the GPIO choice and active high/low state can be easily configured in config.txt, or for custom hardware the overlay can be loaded from the ID EEPROM -- users wouldn't need to compile custom dt-blob.bin files to configure GPIO states at boot.

Thanks for mentioning the possibilities of "pull_low" and "pull_high" instead of "no_pull" in dt-blob.bin. I have not experimented with how these options may change the pin states during boot/reboot, but it would be good to look at just to see how it works.

Re: Overlay for on/off push-button controller.

Posted: Mon Jun 29, 2015 1:06 pm
by paulv
Thanks sparky, that is clear to me now.
I will try this out and compare it with the method I discovered to see if there are differences in the results and report them here. I will also make a new how-to post to allow searches for creating this GPIO based "halt state signal".

In the mean-time, I have ordered a couple of the LTC2951 chips to experiment with. Unfortunately, they are not that easy to get rather quickly without paying horrendous shipping charges. Stay tuned.

Paul

Re: Overlay for on/off push-button controller.

Posted: Tue Jun 30, 2015 6:59 am
by sparkyPi
@Paul: Sounds good! I will look forward to your update!

Best,
sparkyPi

Re: Overlay for on/off push-button controller.

Posted: Wed Jul 01, 2015 2:00 pm
by paulv
[Edited!]
Hi Sparky and Phil,

I finally found some time to pick-up my analysis of the new gpio-poweroff overlay.
Although I got it to work following Sparky's tips, I observed something to keep in mind and also noticed two behaviour changes, or side effects.

Here is what I did.
I added this line to my /boot/config.txt:
dtoverlay=gpio-poweroff, gpiopin=25, active_low
I use gpio pad/pin 25. It produced the following results:
Poweroff set delay.JPG
Poweroff set delay.JPG (33.8 KiB) Viewed 13560 times
Note the very long time it took to initialize the pad (approx. 3.5 seconds). Compared to the TXD signal it looks like this:
Poweroff set delay vs TXD.JPG
Poweroff set delay vs TXD.JPG (21.86 KiB) Viewed 13560 times
This is the actual result after the system is halted:
IMG_2151.JPG
Halted
IMG_2151.JPG (33.83 KiB) Viewed 13514 times
Active high is the exact opposite. Note that the TXD signal no longer goes low. For about 3 mSec, it transmits a message and then goes high again. The (new) message that goes to the console after it reports the "Will now halt" message is "[lapse time] reboot: Power down". You can't use this pin anymore to signal a halt.

Another feature that cannot be used anymore is to reboot the Pi from the halted state by shortening SCL0 or GPIO-4 to GND. Using P6 is now the only method, apart from pulling the plug.

In a next post I'll detail a complete solution when using the dt-blob method, although you have to use a MOSFET to make it work reliably for the /KILL input.

Stay tuned!
paulv
[I had to edit this post because I did not include the active_low option in the text above and when I investigated the signal some more, I never saw the glitch on the GPIO pin anymore]

Re: Overlay for on/off push-button controller.

Posted: Wed Jul 01, 2015 8:21 pm
by paulv
Here is the link to the new post I created :
viewtopic.php?f=107&t=114975

Re: Overlay for on/off push-button controller.

Posted: Thu Jul 02, 2015 6:49 am
by paulv
PhilE, I noticed that the overlay README file (rpi-update yesterday) does not have the gpio-poweroff information yet. Technically, it is no longer an overlay anymore, but this was the place where the .bin file resided. It would be nice if the parameters are explained with an instruction on how to invoke it.

Re: Overlay for on/off push-button controller.

Posted: Thu Jul 02, 2015 7:50 am
by PhilE
Are you sure? It has been in there for the last two releases - I'm on 19debdef5d132a12bf8df73d5e77e607066c8757 now. What does your /boot/.firmware_revision say?

Re: Overlay for on/off push-button controller.

Posted: Thu Jul 02, 2015 9:31 am
by paulv
Hi Phil,
My bad, I looked before didn't see it, looked again just now (I'm on the same firmware revision), but only with grep did I find it.
So sorry!

Paul

Re: Overlay for on/off push-button controller.

Posted: Thu Jul 02, 2015 9:32 am
by PhilE
No problem - I'd rather have a false alarm than leave a problem undetected.

Re: Overlay for on/off push-button controller.

Posted: Thu Jul 02, 2015 11:18 am
by paulv
Phil,

Any idea why it takes 3.5 seconds into the boot procedure before the gpio-poweroff setting gets activated?
One would think that the GPU is taking care of that, so it should be within the first 700mSec. or so.
Just trying to understand & learning...

paulv

Re: Overlay for on/off push-button controller.

Posted: Thu Jul 02, 2015 1:09 pm
by PhilE
That's a good question. The answer is that the "Device Tree" processing is in three parts.

1) dt-blob.bin (either the external file or the default one compiled into the start*.elf binaries) is read by the firmware to allow it to set up the initial pin configuration (and perhaps a few clocks as well). Note that although this has the syntax of a DT file and is compiled using dtc, it isn't really Device Tree as most people would know it (hence the scare quotes above).

2) After loading the kernel, the firmware loads the base .dtb file (e.g. bcm2709-rpi-2-b.dtb) and applies any parameters and overlays, then starts the ARM running to boot the kernel, passing in the combined DT blob.

3) A few seconds into booting, the kernel reads the DT blob, creating platform devices and applying pinctrl settings. This is when the gpio-poweroff settings take effect.

I found (theoretically - I don't have an external switch) that using an active-high KILL signal masked this delay, and would probably eliminate the problem of a restart accidentally powering-off the Pi, but I think you found otherwise.