BBBの電源ボタンでのshutdown抑制

BeagleBone Blackは標準では電源ボタンを8秒未満押した場合shutdownするようになっているが、電源ボタンを外部に引き出して用いている場合にこの挙動が好ましくない場合があります。
昔はACPIから呼ばれるスクリプトでshutdownが行われていたので、このファイルを弄ることで、shutdownをdisableにしたり、任意の動作を行わせることが可能でした。(詳細忘れましたが)

昨今のバージョン(4.19.94-ti-r42)ではこの挙動が変更されており、どうやらシェルスクリプトが屋ばれる仕組みではなくなったようなので、shutdownをdisableにする方法について以下の通り調査した。

まず、この機能は、PMICからのIRQ割込みによって実現されている。OS状の割込みの統計情報は/proc/interruptで確認出来る。

root@beaglebone:~# cat /proc/interrupts
           CPU0
 16:    1524148      INTC  68 Level     gp_timer
 18:          0      INTC   3 Level     arm-pmu
 19:          1      INTC  78 Level     wkup_m3_txev
 20:       5023      INTC  12 Level     49000000.edma_ccint
 22:         51      INTC  14 Level     49000000.edma_ccerrint
 26:          0      INTC  96 Level     44e07000.gpio
 27:          0      INTC  98 Level     4804c000.gpio
 28:          0      INTC  32 Level     481ac000.gpio
 29:          0      INTC  62 Level     481ae000.gpio
 30:         14      INTC  72 Level     44e09000.serial
 35:       6328      INTC  46 Level     481aa000.serial
 36:        496      INTC  70 Level     44e0b000.i2c
 37:          0      INTC  71 Level     4802a000.i2c
 38:        102      INTC  30 Level     4819c000.i2c
 39:        673      INTC  64 Level     mmc0
 40:      16054      INTC  28 Level     mmc1
 44:          0      INTC  77 Level     wkup_m3
 50:          0      INTC  75 Level     rtc0
 51:          0      INTC  76 Level     rtc0
 52:          0      INTC  65 Level     48030000.spi
 53:          0      INTC 125 Level     481a0000.spi
 55:       1456      INTC  41 Level     4a100000.ethernet
 56:     121623      INTC  42 Level     4a100000.ethernet
 59:          0      INTC 109 Level     53100000.sham
 61:          0      INTC 111 Level     48310000.rng
134:          0      INTC  79 Level     eqep_interrupt
136:          0      INTC  88 Level     eqep_interrupt
138:          0      INTC  89 Level     eqep_interrupt
139:          0  44e07000.gpio   6 Edge      48060000.mmc cd
140:          0      INTC  18 Level     musb-hdrc.0
141:          1      INTC  19 Level     musb-hdrc.1
142:          0      INTC  17 Level     47400000.dma-controller
143:          2      INTC   7 Level     tps65217-irq
145:          2  tps65217   0 Edge      vbus

146:          0  tps65217   2 Edge      tps65217_pwr_but <- これ

IPI0:          0  CPU wakeup interrupts
IPI1:          0  Timer broadcast interrupts
IPI2:          0  Rescheduling interrupts
IPI3:          0  Function call interrupts
IPI4:          0  CPU stop interrupts
IPI5:          0  IRQ work interrupts
IPI6:          0  completion interrupts

146がIRQ#となっているようだ。

割込みハンドラを削除する

割込みハンドラが登録されているのであれば、その登録を削除すれば良いのではないか。という事で、既存の割込みハンドラがあるか調べた。割込みハンドラはスケジューラーが管理しておらず、登録すると、割込みハンドラのコンテキストがメモリ上に常駐していて、割込みがあると直ちに実行されるという事らしい。kenrnelスレッドととしてpsコマンドで表示する事が可能で、スレッド名は[irq/IRQ#-name]となるとの事。
残念ながら、そのようなkernelスレッドは存在していないようなのでこの方法は断念。

root@beaglebone:~# ps -ef | grep irq
root         9     2  0 15:04 ?        00:00:00 [ksoftirqd/0]
root        29     2  0 15:04 ?        00:00:00 [irq/37-4802a000]
root        30     2  0 15:04 ?        00:00:00 [irq/38-4819c000]
root        76     2  0 15:04 ?        00:00:00 [irq/139-4806000]
root       109     2  0 15:04 ?        00:00:00 [irq/36-44e0b000]
root       110     2  0 15:04 ?        00:00:00 [irq/143-tps6521]
root      1285  1115  0 15:46 pts/0    00:00:00 grep irq

OSの機能で、割込みを禁止する

Linuxには割込みを特定のCPUにのみ許す設定をする機能がある。
/proc/irc//smp_affinityで割込みを受け付けるCPUを無し(=0)に設定する事で、無効化できるはずだ。
BBBではこの方法は不可であった。そもそもファイルへの書き込みが許されていないようだ。

# echo 0 >/proc/irq/146/smp_affinity
-bash: echo: write error: Input/output error

kernel moduleで該当のIRQの割込みハンドラを登録してみる

ハンドラが起動するだけで、shutdownの動作は上書きされないかもしれないが、試してみた。
先ず次のソースとMakefileを用意する。ビルドにはkernel headersをインストールし、そこに含まれるヘッダファイルをincludeする必要がある。Makefileにあるのが定石的な方法の様だ。

root@beaglebone:/tmp# cat c.c
#include 
#include 
#include 
MODULE_LICENSE("GPL");

const static char *name="pwrbut";
const static int irq=146;

static irq_handler_t isrPwrBut(int irq, void *dev_id, struct pt_regs *regs) {
  printk("PWR-BUT is pressed\n");
  return (irq_handler_t)IRQ_HANDLED;
}

int init_module(void) {
    printk("install '%s' for irq %d\n",name,irq);
    return request_irq (irq, (irq_handler_t)isrPwrBut, IRQF_TRIGGER_RISING, name, NULL);
}

void cleanup_module(void) {
    printk("remove '%s'\n",name);
    free_irq(irq, NULL);
}

root@beaglebone:/tmp# cat Makefile
obj-m += c.o

KDIR := /lib/modules/$(shell uname -r)/build

all:
        $(MAKE) -C $(KDIR) M=$(shell pwd) modules

clean:
        make -C /lib/modules/$(shell uname -r)/build M=$(shell pwd) clean

これで良い筈ですが、insmodでエラーになり実行不可。
デバイスごとに定義されているパラメーターが対応していないという事なんだろう。

root@beaglebone:/tmp# insmod c.ko
insmod: ERROR: could not insert module c.ko: Invalid parameters

kernel moduleで該当のIRQの割込みを禁止する

同様にkernel moudeから、特定のIRQ割込みを禁止にすることが出来る(本来は一時的に割込みを禁止にして排他的なしょりや、タイムクリティカルな処理を行う場合の機能)ので、moduleをロードすると割込みが禁止されたままになるようにしてみた。

# cat disable_pwrbut.c

#include 
#include 

const static char *devname="disable_pwrbut";
const static int irq=146;

int init_module(void) {
  disable_irq(irq);
  printk("install '%s' for irq %d\n",devname,irq);
  return 0;
}

void cleanup_module(void) {
  enable_irq(irq);
  printk("remove '%s'\n",devname);
}

# cat Makefile
obj-m += disable_pwrbut.o

KDIR := /lib/modules/$(shell uname -r)/build

all:
        $(MAKE) -C $(KDIR) M=$(shell pwd) modules

clean:
        make -C /lib/modules/$(shell uname -r)/build M=$(shell pwd) clean

# insmod disable_pwrbut.ko

これは上手く動作するようだ。ひとまずこれで事が足りそうだ。

標準のdtbファイルを変更してでDisableにする

ここら辺に記載されている。
具体的には、/boot/dtbs/4.19.94-ti-r42/am335x-boneblack-uboot-univ.dtbを次のように書き換える。
+++ b/src/arm/am335x-bone-common.dtsi
@@ -362,7 +362,7 @@
        };
 
        pwrbutton {
-               status = "okay";
+               status = "disabled";
        };
 
        regulators {
diff --git a/src/arm/tps65217.dtsi b/src/arm/tps65217.dtsi
index 399baaa..552d6b7 100644
--- a/src/arm/tps65217.dtsi
+++ b/src/arm/tps65217.dtsi
@@ -24,7 +24,7 @@
        };
 
        pwrbutton {
-               compatible = "ti,tps65217-pwrbutton";
+               compatible = "ti,tps65217-pwrbutton-disabled";
                interrupts = <2>;
                status = "disabled";
        };
実際に試していないが、この方法であれば可能だろう。