# Enabling standby mode using the power key

**URL:** <https://forum.clockworkpi.com/t/enabling-standby-mode-using-the-power-key/5695>\
**Category:** Developers\
**Created:** [May 23, 2020, 6:34pm UTC](https://forum.clockworkpi.com/t/enabling-standby-mode-using-the-power-key/5695 "2020-05-23T18:34:20Z")\
**Posts on this page:** 3\
**Page:** 1

<div class="post-metadata">

**Author:** ![Joao\_Manoel](https://avatars.discourse-cdn.com/v4/letter/j/fbc32d/32.png) [@Joao\_Manoel](https://forum.clockworkpi.com/u/Joao_Manoel)\
**Post date:** [May 23, 2020, 6:34pm UTC](https://forum.clockworkpi.com/t/enabling-standby-mode-using-the-power-key/5695/1 "2020-05-23T18:34:20Z")

</div>

Hello,

Months ago while working in our Kernel, I managed to enable the power key functionality to do a soft power off of the game shell ([How to get the power button to soft power off](https://forum.clockworkpi.com/t/how-to-get-the-power-button-to-soft-power-off/5201)). To enable it, the change was simple: I just removed the power key code (in `drivers/input/misc/axp20x-pek.c`, and `drivers/mfd/axp20x.c`) that was in our patch (`52rc4.patch`) and used the the mainline kernel without these changes. Maybe our kernel devs needed this before, but it is not needed anymore in recent kernel, I don’t know.

This is a very convenient way to switch off the gameshell but what I really wanted was a standby mode to conserve power.

Recently I started to investigate what I could do to get standby working. The first thing I did was to set `mem` to `/sys/power/state` with `echo mem > /sys/power/state`. This will trigger a standby routine and doing it the gameshell enters in a sort of sleep mode. It was very promising, the problem was that I was not able to get it back from the sleep state with the power key and I needed to hold the power key for more than 7~10 seconds for a hardware power off. Another way to get it back would be using the RTC to trigger a wakeup routine. The problem was that the RTC was not enabled as wake up source in our kernel (5.2rc5), so I couldn’t test it. On recent kernels (\>5.5) I found that the RTC wake functionality was finally implemented, and I decided to try it. The easiest way to do it is using the command `rtcwake`, so I first sent:

`#rtcwake -m mem -s 60`

To enter in standby (`mem` state) , and wakeup after 60 seconds. It worked 🙂

Now the problem is to make it wakeup using the power key. I found the developer that made a lot of contributions to the mainline linux kernel for the Allwinner SOC’s, and our PMIC. He is working actively on improving battery life on Pinephone, and the Pinebook that uses Allwinner SOC. He suggested me to try using one of his kernel branches to enable a irq that maybe would wakeup our SOC with the power key.

The branch is `patch/irqchip-v2` from `https://github.com/smaeul/linux`. I applied my gameshell patch for the kernel 5.7rc5 and compiled his kernel. It worked!!! Finally the power key can wake up! The downside was that it switches off immediately after waking up. The problem was that the power key still sending a `KEY_POWER` to the userspace. But to solve it was easy: Just change what the power key sends to userspace on `drivers/input/misc/axp20x-pek.c `. Doing so I cannot have the power key for power off. Not happy yet.

Looking in our PMIC datasheet I found that it can identify four different events coming from power key: `falling edge`, `rising edge`, `short press`, and `long press`. So I modified the kernel code to instead of send a `KEY_POWER` event, it would send a `KEY_SUSPEND` for `short press`, and `KEY_POWER` for `long press`. At first I though that as soon the SOC wakes, it would trigger again a sleep event like before, but to my surprise it didn’t happen. When the system wakes, it doesn’t trigger another sleep event and all worked 😃. A `short press` trigger a suspend event and wakes up, a `long press` trigger a power off. Perfect.

I tried to understand what was happening but the datasheet was not very clear, I found that our PMIC can wake up from `falling edge`, but `short press` was not in the list. So maybe when it wakes up it reports to the userspace that an `falling edge` occurred, but don’t report `short press`. I know that it is impossible to have a `falling edge` without a `short press`, but it is how things seems to be working. I couldn’t find a better explanation, you can comment if you have a better idea.

Here are my changes to the kernel code:

```auto
diff --git a/drivers/mfd/axp20x.c b/drivers/mfd/axp20x.c
index aa59496e4..3653f4bf4 100644
--- a/drivers/mfd/axp20x.c
+++ b/drivers/mfd/axp20x.c
@@ -202,8 +202,10 @@ static const struct resource axp803_usb_power_supply_resources[] = {
 };

 static const struct resource axp22x_pek_resources[] = {
- DEFINE_RES_IRQ_NAMED(AXP22X_IRQ_PEK_RIS_EDGE, "PEK_DBR"),
- DEFINE_RES_IRQ_NAMED(AXP22X_IRQ_PEK_FAL_EDGE, "PEK_DBF"),
+// DEFINE_RES_IRQ_NAMED(AXP22X_IRQ_PEK_RIS_EDGE, "PEK_DBR"),
+// DEFINE_RES_IRQ_NAMED(AXP22X_IRQ_PEK_FAL_EDGE, "PEK_DBF"),
+ DEFINE_RES_IRQ_NAMED(AXP22X_IRQ_PEK_LONG, "PEK_DBF"),
+ DEFINE_RES_IRQ_NAMED(AXP22X_IRQ_PEK_SHORT, "PEK_DBR"),
 };

 static const struct resource axp288_power_button_resources[] = {

```

and

```auto
diff --git a/drivers/input/misc/axp20x-pek.c b/drivers/input/misc/axp20x-pek.c
index c8f87df93..7aa7591ad 100644
--- a/drivers/input/misc/axp20x-pek.c
+++ b/drivers/input/misc/axp20x-pek.c
@@ -212,10 +212,16 @@ static irqreturn_t axp20x_pek_irq(int irq, void *pwr)
         * The power-button is connected to ground so a falling edge (dbf)
         * means it is pressed.
         */
- if (irq == axp20x_pek->irq_dbf)
+ //Using long press for KEY_POWER instead of falling edge
+ //Using short press for KEY_SUSPEND
+ if (irq == axp20x_pek->irq_dbf) {
                input_report_key(idev, KEY_POWER, true);
- else if (irq == axp20x_pek->irq_dbr)
                input_report_key(idev, KEY_POWER, false);
+ }
+ else if (irq == axp20x_pek->irq_dbr) {
+ input_report_key(idev, KEY_SUSPEND, true);
+ input_report_key(idev, KEY_SUSPEND, false);
+ }

        input_sync(idev);

@@ -252,6 +258,7 @@ static int axp20x_pek_probe_input_device(struct axp20x_pek *axp20x_pek,
        idev->dev.parent = &pdev->dev;

        input_set_capability(idev, EV_KEY, KEY_POWER);
+ input_set_capability(idev, EV_KEY, KEY_SUSPEND);

        input_set_drvdata(idev, axp20x_pek);

```

So, now comes the part of understanding our sleep mode.  
Looking the contents in `/sys/power/mem_sleep` we only see `[s2idle]` and no other modes. To understand the modes we can look at:

[https://www.kernel.org/doc/Documentation/power/states.txt](https://www.kernel.org/doc/Documentation/power/states.txt)

> State: Suspend-To-Idle  
> ACPI state: S0  
> Label: “s2idle” (“freeze”)
> 
> This state is a generic, pure software, light-weight, system sleep state.  
> It allows more energy to be saved relative to runtime idle by freezing user  
> space and putting all I/O devices into low-power states (possibly  
> lower-power than available at run time), such that the processors can  
> spend more time in their idle states.
> 
> This state can be used for platforms without Power-On Suspend/Suspend-to-RAM  
> support, or it can be used in addition to Suspend-to-RAM to provide reduced  
> resume latency. It is always supported.

Which means that it doesn’t switches off the processor, and is doesn’t suspend to RAM (`[deep]` state). It freezes all the processes. It is like having everything on but with load at 0%. The CPU works at lowest possible level.

The developer (smaeul) told me that he is working in a open source code to develop deep state for Allwinner SOC named **crust-firmware** ([GitHub - crust-firmware/crust: SCP (power management) firmware for sunxi SoCs](https://github.com/crust-firmware/crust)), but what I understood from him was that deep state is not implemented in our Allwinner R16 SOC:

> …Deep suspend requires [extra firmware](https://github.com/crust-firmware/crust) that runs while the CPUs are off, and turns the CPUs back on when an interrupt comes in. That firmware doesn’t exist for R16 on mainline Linux. It looks like U-Boot (your PSCI implementation) always returns “Not Implemented” for `ARM_PSCI_1_0_FN_PSCI_FEATURES` , which means Linux should detect it as not supporting suspend…

crust-firmware relies on a always-on [AR100](https://linux-sunxi.org/AR100#Power_management_firmware) microcontroller built-in inside the SOC. This microcontroller is responsible of waking up the CPU, GPU… and provides power management services.

The bad part is that the AR100 seems to not be present inside our R16 SOC. Maybe our R16 doesn’t support `deep` state in hardware. If someone can write something about it would be very good to understand if we have it implemented in hardware or not, and if yes where we can get the firmware that enables it (even if it is not open source).

Meanwhile we can use the power key, and the `s2idle` mode, I think this is already a good start. So I did some experiments to see what we get.

I charged the gameshell to 100% and monitored the power consumption. The CPU load was less than 5% for the entire period, wifi was on. I took measurements each 5 minutes for different LCD brightness configurations (9 (max), 5, and 0 (swithed off)). For the `freezing mode` I took measurements with 1 hour intervals (I woke up the gameshell, measured, and started the standby again). Keep in mind that I’m using the original battery, and my battery is already ~1 year old.

To change brightness I used:

`#echo n > /sys/class/backlight/backlight/brightness`

where `n` is a number between 0, and 9. 0 means off, and 9 is the max brightness.

To take measurements I created a small script to write it in a file and used a cronjob to execute it every 5 minutes:

```auto
#!/bin/bash
cat /sys/class/power_supply/axp20x-battery/voltage_now | tr -d '\n' >> bat_current.txt
echo -en '\t' >> bat_current.txt
cat /sys/class/power_supply/axp20x-battery/capacity | tr -d '\n' >> bat_current.txt
echo -en '\t' >> bat_current.txt
cat /sys/class/power_supply/axp20x-battery/current_now | tr -d '\n' >> bat_current.txt
echo -en '\t' >> bat_current.txt
date +%H:%M >> bat_current.txt

```

This script generates a line in a text file with the battery voltage, capacity, current, and the actual time separated by a tab.

 ![untitled](https://canada1.discourse-cdn.com/flex029/uploads/clockworkpi/original/2X/d/df86a26feb75916a618c771bfa254177babf5fe7.png)

The results clearly states that when `freezing` we didn’t use a `deep state` mode or `suspend to RAM`, but it is the better scenario. You ensure that the CPU won’t do any job, switches off the screen, the network and you can return to your work in 3 seconds. A deeper state will probably need something like 8-10 seconds to return to life.

For curiosity I also plotted the power consumed over time.

 ![discharge_scrennoff](https://canada1.discourse-cdn.com/flex029/uploads/clockworkpi/original/2X/d/dba88e63674c307233bddfe107c062cd3850e08b.png)

Edit: An extra information can be taken from the graph: The battery capacity in mAh. For this we can integrate the blue trace over time. In this case, for approximation since it is stable, we can just multiply the averaged power (0.86 W) by the time that the battery lasted (375 min.). We get:

`(375/60)x0.86/3.7 = 1.45 Ah = 1450 mAh`

This is very good, and my battery, despite its time, still holding a very good amount of charge. Also it’s better than the specs that should be 1200 mAh. I think, based on this measurement, our battery is 1500 mAh instead.

The measurement is quite stable, but for the other LCD options (5, and 9 levels) the results were unstable. Probably because the CPU still can do tasks, and the power consumption varies over time. Different of what happens in freezing, where no tasks are executed.

In summary, the freezing mode using the power key is a pause/unpause button and you can return to your game very fast. Your game will be frozen like all the system. This is very convenient when you are for instance changing trains, waiting for the bus when moving in the city, or need to talk to someone for a few minutes… But not enough for many hours of pause. In this case switching off is better (with long key press 🙂 ).

For these experiments I also used my `dtb` files that allows higher frequency of operations ([1.2GHz and beyond - #27 by Joao\_Manoel](https://forum.clockworkpi.com/t/1-2ghz-and-beyond/5253/27)). It enables lower voltage operations for the CPU. The original `dtb` allows lower CPU frequencies, but keep applying higher voltage to the CPU.

If we get more information about the deep state mode we can dream a bit more.

In this thread “[How do I come to suspend?](https://forum.clockworkpi.com/t/how-do-i-come-to-suspend/1474)”, @hal mentioned:

> [@How do I come to suspend?](https://forum.clockworkpi.com/t/how-do-i-come-to-suspend/1474/3):
>
> We have tried this feature and we can suspend most of the parts, but unfortunately we can’t suspend the CPU, because there is a coprocessor driver that is not open source, that means the entire system will waste more than 100mAh, simply put, the battery will be out of power after about 10 hours…  
> We can’t accept this result, so we removed this feature.

I think 10 hours is amazing, and very acceptable (for me at least), maybe he can help us to improve what I did further with what he tried before. Also, how we could use the non-open source firmware to get a deeper state? Maybe we could modify a bit the **crust-firmware** and make it work for the R16.

I apologies for language mistakes here, I’m not good at writing in English.

---

<div class="post-metadata">

**Author:** ![r043v](https://yyz1.discourse-cdn.com/flex029/user_avatar/forum.clockworkpi.com/r043v/32/672_2.png) [@r043v](https://forum.clockworkpi.com/u/r043v)\
**Post date:** [May 23, 2020, 10:09pm UTC](https://forum.clockworkpi.com/t/enabling-standby-mode-using-the-power-key/5695/2 "2020-05-23T22:09:55Z")

</div>

glad to see the deep search and effort you made into this, that’s very promising (and was pretty pleasant to read

---

<div class="post-metadata">

**Author:** ![Joao\_Manoel](https://avatars.discourse-cdn.com/v4/letter/j/fbc32d/32.png) [@Joao\_Manoel](https://forum.clockworkpi.com/u/Joao_Manoel)\
**Post date:** [May 24, 2020, 12:34am UTC](https://forum.clockworkpi.com/t/enabling-standby-mode-using-the-power-key/5695/3 "2020-05-24T00:34:13Z")

</div>

Thanks 🙂  
The text have many language mistakes, I’ll edit to improve it often.

I didn’t expect to have it long, but wanted to document the steps properly so someone else can help us to improve it.
