linuxlinux-kernelhaltpower-off

How to make linux power off when halt is run?


I have successfully used the pm_power_off function pointer to make my custom Linux board call its power management chip over i2c (to turn the power off).

I would like the Linux halt command to switch the power off too.

How can I achieve this?

The (ARM) code for machine_halt does not have an pointer analogous to machine_power_off's pm_power_off.

arch/arm/kernel/reboot.c:

/*
 * Halting simply requires that the secondary CPUs stop performing any
 * activity (executing tasks, handling interrupts). smp_send_stop()
 * achieves this.
 */
void machine_halt(void)
{
    local_irq_disable();
    smp_send_stop();

    local_irq_disable();
    while (1);
}

/*
 * Power-off simply requires that the secondary CPUs stop performing any
 * activity (executing tasks, handling interrupts). smp_send_stop()
 * achieves this. When the system power is turned off, it will take all CPUs
 * with it.
 */
void machine_power_off(void)
{
    local_irq_disable();
    smp_send_stop();

    if (pm_power_off)
        pm_power_off();
}

I could obviously just hack machine_halt, but I would like to do this "properly", if possible.

Have I missed something (perhaps in userspace) which could cause a halt command to execute a "power off"?


Update: Thanks for the answer and all of your comments, they have helped me realise what the actual issue is.

My problem is:

I have an input edge, available to the custom power management unit. Think of it as a start button, with no stop or reset functionality. I have full control of the PMU code (it's an ATMEGA running as an i2c slave).

  1. If the Linux kernel is running, I want the edge to be ignored.
  2. If the CPU is powered off, I want the edge to power-on the CPU.
  3. If the Linux kernel is halted, I want the edge to reset the CPU.

Case 1 is easy, nothing to do.

Case 2 was easy, define pm_power_off in my driver to send an i2c message to the PMU. Luckily the i2c subsystem is still in a working state when pm_power_off is called.

Case 3 is the problem - I was looking for a pm_halt to define, to send an i2c message to the PMU.

Perhaps there is another way, as 0andriy comments?

Is there a place in the kernel to keep prodding the PMU with i2c messages at a few Hz, unless the machine is halted?

The answer at: https://unix.stackexchange.com/a/42576/17288 reads:

"* These days halt is smart enough to automatically call poweroff if ACPI is enabled. In fact, they are functionally equivalent now."

Perhaps there is some way of providing or hooking into ACPI - I will have to read up on it.


Solution

  • You should use poweroff command or halt -p. As per man 8 halt, halt command (with no arguments) doesn't guarantee to power off your machine. Reasons are described here:

    halt was used before ACPI (which today will turn off the power for you)*. It would halt the system and then print a message to the effect of "it's ok to power off now". Back then there were physical on/off switches, rather than the combo ACPI controlled power button of modern computers.

    *These days halt is smart enough to automatically call poweroff if ACPI is enabled. In fact, they are functionally equivalent now.

    As you can see from halt tool source code, it issues reboot() system call with cmd = RB_POWER_OFF = LINUX_REBOOT_CMD_POWER_OFF.

    In kernel, that system call is implemented here, and on cmd = LINUX_REBOOT_CMD_POWER_OFF, it calls:
       -> kernel_power_off()
       -> machine_power_off()
       -> pm_power_off()