Armadilloフォーラム

スリープからのウェイクアップのボタンの設定

yse

2014年11月19日 13時09分

お世話になります。
山田と申します。

現在Armadillo-410 液晶モデル開発セットを使っています。
スリープ機能でsuspend-to-RAMの状態からウェイクアップするために、デフォルトでLCDボードのSW1~3(GPIO2_20、29,30)などが使用可能ですが、
代わりに別のGPIOで行うことはできるのでしょうか?
例えば、410拡張ボードのCON9_28(GPIO3_15)でウェイクアップさせることは可能でしょうか?

410拡張ボードのSW1がCON15_43に出ているので、そこにSWをつけてもいいのですが、できれば基板に端子をはんだ付けせずにCON9からコネクタでSWを取りたいと思ったため質問させていただきました。
よろしくお願い致します。

コメント

中村です。

> 例えば、410拡張ボードのCON9_28(GPIO3_15)でウェイクアップさせることは可能でしょうか?

やったことはありませんが、たぶん次のようにすれば
できるのではないかと思います。

Linuxソースのarch/arm/mach-mx25/armadillo400.c
に次のコードがあります。

static struct gpio_keys_button armadillo400_key_buttons[] = {
        {KEY_ENTER, GPIO(3, 30), 1, "SW1",     EV_KEY, CONFIG_ARMADILLO400_SW1_GPIO_3_30_IS_WAKE_SRC},
#if defined(CONFIG_MACH_ARMADILLO410) || defined(CONFIG_MACH_ARMADILLO440) || defined(CONFIG_MACH_ARMADILLO460)
        {KEY_BACK,  GPIO(2, 20), 1, "LCD_SW1", EV_KEY, CONFIG_ARMADILLO400_CON11_39_GPIO_2_20_IS_WAKE_SRC},
#if defined(CONFIG_ARMADILLO400_CON11_40_GPIO_2_29)
        {KEY_MENU,  GPIO(2, 29), 1, "LCD_SW2", EV_KEY, CONFIG_ARMADILLO400_CON11_40_GPIO_2_29_IS_WAKE_SRC},
#endif
#if defined(CONFIG_ARMADILLO400_CON11_41_GPIO_2_30)
        {KEY_HOME,  GPIO(2, 30), 1, "LCD_SW3", EV_KEY, CONFIG_ARMADILLO400_CON11_41_GPIO_2_30_IS_WAKE_SRC},
#endif
#endif /* CONFIG_MACH_ARMADILLO410 || CONFIG_MACH_ARMADILLO440 || CONFIG_MACH_ARMADILLO460 */
};
 
static struct gpio_keys_platform_data armadillo400_gpio_key_data = {
        .buttons = armadillo400_key_buttons,
        .nbuttons = ARRAY_SIZE(armadillo400_key_buttons),
        .wakeup_default_disabled = !CONFIG_ARMADILLO400_GPIO_KEYS_IS_WAKE_SRC,
};

ここにCON9_28(GPIO3_15)を追加してあげれば良さそうです。
ここに追加する場合、そのGPIOが他の部分で別用途に
使用されないように注意してください。

--
なかむら

返信、ありがとうございます。
ただ、ソースをいじるのは難しそうなので、いまのところ素直にSW1を使う方向で検討しています。
山田

毎度お世話様、伊澤@ITTOです。
こちらでもGPIOでwakeupする必要ができたので、中村さんの指摘をヒントに
armadillo400_key_buttonsに以下の二行を追加してみました。
--

--
    {KEY_BACK,    GPIO(1, 17), 1, "SHUT_ST", EV_KEY, 1},
    {KEY_MENU,    GPIO(1, 31), 1, "SW1_2",   EV_KEY, 1},
--

--
KEY_BACKなどのシンボルは既存のものを適当につけてみました。
また、文字列もこちらの都合でつけてみました。
それでカーネルを構築して試したところ……
巧くいきませんでした。
やっぱり一筋縄ではいきませんね。

どなたか知見はございませんでしょうか。

やったことはないけど・・・と無責任な投稿をしていた中村です。

今、出先でArmadillo本体も資料もソースもないのですが、
まず、デフォルトカーネルでwakeupできることになっている
GPIO(3, 30)のSW1(Armadillo-420や440のタクトSWだったと思います)で
wakeupできてますか?

次に、SW1のeventを受け取るのと同じ方法で、
> {KEY_BACK, GPIO(1, 17), 1, "SHUT_ST", EV_KEY, 1},
> {KEY_MENU, GPIO(1, 31), 1, "SW1_2", EV_KEY, 1},
これらの独自ボタンeventで受け取れますか?
スリープさせずに通常動作させた状態で、
たとえばevtestコマンド(ってのがありましたよね)でです。

--
なかむら

毎度お世話様、伊澤です。
以下引用にて。
--
> やったことはないけど・・・と無責任な投稿をしていた中村です。
>
いえいえ、助かっています。

> 今、出先でArmadillo本体も資料もソースもないのですが、
> まず、デフォルトカーネルでwakeupできることになっている
> GPIO(3, 30)のSW1(Armadillo-420や440のタクトSWだったと思います)で
> wakeupできてますか?
>
これは問題ありません。sw1でwakeupしてますので。
そうそう、echo enabled > /sys/devices/platform/gpio-keys.0/power/wakeup
してから echo standby > /sys/power/state しています。

> 次に、SW1のeventを受け取るのと同じ方法で、
> > {KEY_BACK, GPIO(1, 17), 1, "SHUT_ST", EV_KEY, 1},
> > {KEY_MENU, GPIO(1, 31), 1, "SW1_2", EV_KEY, 1},
> これらの独自ボタンeventで受け取れますか?
> スリープさせずに通常動作させた状態で、
> たとえばevtestコマンド(ってのがありましたよね)でです。
>
あー、その辺りは失念していました。既に帰宅しているので、明日トライします。

度々伊澤です。
どうやら巧くいったようです。armadillo400_key_buttonsに

--
{KEY_BACK,    GPIO(1, 17), 1, "SHUT_ST", EV_KEY, 1},
{KEY_MENU,    GPIO(1, 31), 1, "SW1_2",   EV_KEY, 1},
--

を追加したために、gpio_list_revcの

--
{"CON9_11",  GPIO(1, 17), MXC_EXT_GPIO_DIRECTION_INPUT},
{"CON9_16",  GPIO(1, 31), MXC_EXT_GPIO_DIRECTION_INPUT},
--

と競合したのかgpio-keysのprobeの際にエラーになっていたようです。
上記をコメントアウトして、無事にevtestもwakeupもできるようになりました。
勿論その代わりに、/sys/class/gpio/CON9_11が使えなくなりましたが。
レベル確認とwakeup要因の確認に使っていたのでできれば共存させたいのですが……

何か回避策があれば、ご教示願います。

中村です。

> 度々伊澤です。
> どうやら巧くいったようです。armadillo400_key_buttonsに

動作報告、ありがとうございます。
あの方法でOKだったようですね。

>

> --
> {"CON9_11",  GPIO(1, 17), MXC_EXT_GPIO_DIRECTION_INPUT},
> {"CON9_16",  GPIO(1, 31), MXC_EXT_GPIO_DIRECTION_INPUT},
> --
> 

> と競合したのかgpio-keysのprobeの際にエラーになっていたようです。
> 上記をコメントアウトして、無事にevtestもwakeupもできるようになりました。

コメントアウトされたこの部分、
ifdefでCONFIG_XXXXをみて、他の用途に使ってなければ・・・
というコードになってます。
一時的な試験ではコメントアウトでもいいですが、
Kconfigに自分用の設定を書き足して、gpio-keysの
> {KEY_BACK, GPIO(1, 17), 1, "SHUT_ST", EV_KEY, 1},
> {KEY_MENU, GPIO(1, 31), 1, "SW1_2", EV_KEY, 1},
を有効にするところと、普通のgpioから外すところを
ifdefにしてあげるといいと思います。

> 勿論その代わりに、/sys/class/gpio/CON9_11が使えなくなりましたが。
> レベル確認とwakeup要因の確認に使っていたのでできれば共存させたいのですが……

gpio-keysにしてしうと、gpioのSYSFSでは使えなくなってしまいます。

gpioのSYSFSのままwakeup-srcにできるといいのですが、
カーネル2.6.26にはその機能はなさそうな気がします。
(見落としてるかもしれません。どなたかご存知ですか?)

カーネル3.14の
linux-3.14-at4/drivers/gpio/gpio-mxc.c
に、それっぽいコードがありました。

--
なかむら

中村です。

興味があったので、GPIOでwakeupする実験をしてみました。
gpio-keysにせずにです。

CON9_11決め打ちのコードですが、これでwakeupはしました。

常時ONでも良かったのですが、GPIOのエッジ割り込みの設定が
fallingかbothのときのみwakeupをenableするようにしてみました。

# echo falling > /sys/class/gpio/CON9_11/edge
# echo mem > /sys/power/state
としてから、CON9_11をLOWにすると目を覚まします。

修正は、linux-2.6.26-at25/drivers/gpio/gpiolib.cです。

--- gpiolib.c-orig      2015-04-22 15:39:57.000000000 +0900
+++ gpiolib.c   2015-12-05 03:21:10.000000000 +0900
@@ -62,6 +62,7 @@
 #if defined(CONFIG_DEBUG_FS) || defined(CONFIG_GPIO_SYSFS_PRIVATE_NAMING)
        const char              *label;
 #endif
+       int enabled_irq_wake;
 };
 static struct gpio_desc gpio_desc[ARCH_NR_GPIOS];
 
@@ -322,6 +323,21 @@
        if (irq < 0)
                return -EIO;
 
+       if (strcmp(desc->label, "CON9_11") == 0) {
+           if (test_bit(FLAG_TRIG_FALL, &gpio_flags)) {
+               if (!desc->enabled_irq_wake) {
+                   desc->enabled_irq_wake = 1;
+                   enable_irq_wake(irq);
+               }
+           } else {
+               if (desc->enabled_irq_wake) {
+                   // see kernel/irq/manage.c set_irq_wake();
+                   desc->enabled_irq_wake = 0;
+                   disable_irq_wake(irq);
+               }
+           }
+       }
+
        id = desc->flags >> PDESC_ID_SHIFT;
        pdesc = idr_find(&pdesc_idr, id);
        if (pdesc) {
@@ -604,6 +620,8 @@
 
        desc = &gpio_desc[gpio];
 
+       desc->enabled_irq_wake = 0;
+
        va_start(vargs, fmt);
        dev = device_create_vargs(&gpio_class, desc->chip->dev, MKDEV(0, 0),
                                  NULL, fmt, vargs);

gpio_descにenabled_irq_wakeというフラグを用意して
disable_irq_wake()するかどうかを判断しているのは、
kernel/irq/manage.cのset_irq_wake()が次のようになっていて、
enableとdisableのコールの数が合わないと、disable時に
警告が(スタックトレースも)がでるためです。

/**
 *      set_irq_wake - control irq power management wakeup
 *      @irq:   interrupt to control
 *      @on:    enable/disable power management wakeup
 *
 *      Enable/disable power management wakeup mode, which is
 *      disabled by default.  Enables and disables must match,
 *      just as they match for non-wakeup mode support.
 *
 *      Wakeup mode lets this IRQ wake the system from sleep
 *      states like "suspend to RAM".
 */
int set_irq_wake(unsigned int irq, unsigned int on)
{
        struct irq_desc *desc = irq_desc + irq;
        unsigned long flags;
        int ret = -ENXIO;
        int (*set_wake)(unsigned, unsigned) = desc->chip->set_wake;
 
        /* wakeup-capable irqs can be shared between drivers that
         * don't need to have the same sleep mode behaviors.
         */
        spin_lock_irqsave(&desc->lock, flags);
        if (on) {
                if (desc->wake_depth++ == 0)
                        desc->status |= IRQ_WAKEUP;
                else
                        set_wake = NULL;
        } else {
                if (desc->wake_depth == 0) {
                        printk(KERN_WARNING "Unbalanced IRQ %d "
                                        "wake disable\n", irq);
                        WARN_ON(1);
                } else if (--desc->wake_depth == 0)
                        desc->status &= ~IRQ_WAKEUP;
                else
                        set_wake = NULL;
        }
        if (set_wake)
                ret = desc->chip->set_wake(irq, on);
        spin_unlock_irqrestore(&desc->lock, flags);
        return ret;
}

                        printk(KERN_WARNING "Unbalanced IRQ %d "
                                        "wake disable\n", irq);
                        WARN_ON(1);

の部分です。

実験では割り込みエッジ設定のときだけenable_irq_wake()と
disable_irq_wake()を呼ぶことしかやっていませんが、
他にもやらなければならないことがあるかもしれません。

gpio_keys.cを見ると、
device_init_wakeup()やdevice_set_wakeup_enable()を
呼んでいるところがあったり、
static int gpio_keys_suspend(struct platform_device *pdev, pm_message_t state)

static int gpio_keys_resume(struct platform_device *pdev)
の中でenable_irq_wake(),disable_irq_wake()したりしています。

--
なかむら

中村です。

補足です。

ほんとの手抜きでやるなら、GPIOデバイスの初期化で
いろいろ終わった(成功した)あとに特定のポートだけ
enable_irq_wake()するのでもいいかもしれません。

GPIOデバイスの初期化というのは、先ほどのパッチで
> + desc->enabled_irq_wake = 0;
> +
をしている関数、
static int gpio_sysfs_create_device(unsigned gpio, bool direction_may_change,
const char *fmt, ...)
です。

この関数の引数fmtに"CON9_11"と入ってますので、
device_create_file()まで正常終了したあとで、
こんな(↓)感じかな?と思います。

    if (!status && strcmp(fmt, "CON9_11") == 0) {
        enable_irq_wake(gpio_to_irq(gpio));
    }

これ以外、gpiolib.cの他の部分の修正はいらないはずです。

--
なかむら

毎度お世話様、伊澤@ITTOです。
中村様、ばっちりです。今回はピン番号も決め打ちなので手抜き版を採用したので、
まさにこれだけですよ。これだけ。
--

--- /tmp/gpiolib.c    2015-12-07 18:06:20.000000000 +0900
+++ kernel/drivers/gpio/gpiolib.c    2015-12-07 18:07:11.000000000 +0900
@@ -616,7 +616,13 @@
         else
             status = device_create_file(dev,
                             &dev_attr_value);
-
+#if 1    // for NAKAMURA special.
+        if (! status &&
+            (strcmp(fmt, "CON9_11") == 0 ||
+             strcmp(fmt, "CON9_16") == 0)) {
+            enable_irq_wake(gpio_to_irq(gpio));
+        }
+#endif
         if (!status && gpio_to_irq(gpio) >= 0
             && (direction_may_change
             || !test_bit(FLAG_IS_OUT, &desc->flags)))

--
これで無事に「死んでしまえ」信号と「寝てはならぬ」信号で起きられるようになりました。