Güç Yönetimi & Thermal
TEKNİK REHBER GÜÇ YÖNETİMİ SUSPEND/RESUME 2026

Suspend & Resume —
Linux PM states.

freeze'den S4 hibernation'a tüm Linux uyku geçişleri: wakeup source yönetimi, driver hook'ları, hata ayıklama ve Raspberry Pi'de RTC ile uyandırma pratiği.

00 Linux PM states — S0'dan S5'e

Linux, ACPI standardından esinlenen bir güç durum modeli kullanır. Her state farklı bir enerji/uyanma gecikmesi tradeoff'u sunar.

S0 — Running
Normal çalışma durumu. CPU ve tüm çevreler aktif. Runtime PM (RPM) bu state içinde çalışır: kullanılmayan donanım blokları bireysel olarak kapatılır.
S0ix — Connected Standby
Modern Intel/AMD dizüstü bilgisayarlar ve bazı ARM SoC'lar. Sistem uyurken ağ/Bluetooth aktif kalabilir. Linux'ta "s2idle" (freeze) olarak uygulanır.
S2RAM — Suspend-to-RAM
RAM güçlü kalır, içerik korunur. CPU ve çevre birimleri kapatılır. Uyanma ~100ms. En yaygın mobil/gömülü uyku modu. Linux'ta "mem" state.
S3 — Standby
ACPI S3, S2RAM ile eşdeğerdir — bazı BIOS'larda ayrı gösterilir. Linux "standby" state'i biraz daha sığdır (standby = hafif uyku, mem = tam S2RAM).
S4 — Hibernate
RAM içeriği swap/disk'e yazılır, sistem tamamen kapatılır. Uyanma ~10-30s (swap'tan okuma süresi). Linux'ta "disk" state. Pil tamamen bitecekse veri kaybı olmaz.
S5 — Soft Off
Sistem tamamen kapalı ama güç kaynağı bağlı. Soğuk önyükleme gerekir. Wakeup on LAN (WoL) bazı sistemlerde bu state'te çalışabilir.

mem state'inin gerçek davranışı

bash
# mem state hangi derinliği kullanıyor?
cat /sys/power/mem_sleep
# s2idle [deep]   → köşeli parantez içindeki aktif
# ya da:
# [s2idle] deep

# deep (S2RAM) yerine s2idle (Connected Standby) seç
echo s2idle > /sys/power/mem_sleep

# gerçek S2RAM için
echo deep > /sys/power/mem_sleep

01 /sys/power/state — geçiş mekanizması

/sys/power/state dosyasına uyku modu adı yazmak, o moda geçişi başlatır. Komut döndüğünde sistem zaten uyanmış demektir.

bash
# desteklenen state'leri görüntüle
cat /sys/power/state
# freeze mem disk

# freeze (s2idle / connected standby)
echo freeze > /sys/power/state

# suspend-to-RAM (S2RAM)
echo mem > /sys/power/state

# standby (hafif uyku, bazı platformlarda)
echo standby > /sys/power/state

# hibernation (swap'a yaz)
echo disk > /sys/power/state

Sistem uyku döngüsü (internal)

Suspend sırası (Linux kernel)
echo mem > /sys/power/state
    │
    ├─ 1. senkronize_filesystems()   — FS flush
    ├─ 2. freeze_processes()         — kullanıcı proc'ları dondur
    ├─ 3. freeze_kernel_threads()    — kernel thread'leri dondur
    ├─ 4. dpm_suspend_start()        — driver .prepare() hook'ları
    ├─ 5. dpm_suspend()              — driver .suspend() hook'ları
    ├─ 6. dpm_suspend_late()         — driver .suspend_late()
    ├─ 7. dpm_suspend_noirq()        — driver .suspend_noirq()
    ├─ 8. disable_nonboot_cpus()     — CPU hotplug off
    ├─ 9. syscore_suspend()          — son sistem çekirdek uyku
    └─10. arch_suspend()             — donanım uyku (PSCI/WFI)
         ...  DONANIM UYKU  ...
    └─10. arch_resume()
    ├─ 9. syscore_resume()
    ├─ 8. enable_nonboot_cpus()
    ├─ 7. dpm_resume_noirq()
    ├─ 6. dpm_resume_early()
    ├─ 5. dpm_resume()
    ├─ 4. dpm_resume_end()
    ├─ 3. thaw_kernel_threads()
    └─ 2. thaw_processes()
DİKKAT

echo mem > /sys/power/state komutu başarıyla döndüğünde sistem artık uyanmış demektir — komut bloke olur, uyanınca devam eder. Hata olursa negatif errno döner ve dmesg'de hangi driver'ın başarısız olduğu görülür.

02 Wakeup sources — kayıt ve yönetim

Sistem suspend halindeyken onu uyandırabilecek kaynaklar "wakeup source" olarak kayıt edilmelidir. Kayıtsız bir interrupt sistemi uyandıramaz.

Kernel API

c — driver içinde wakeup source
#include <linux/pm_wakeup.h>

/* Cihazı wakeup özellikli yap */
device_init_wakeup(&pdev->dev, true);

/* Wakeup event bildirimi (interrupt handler içinde) */
pm_wakeup_event(&pdev->dev, 500);   /* 500ms wakeup lock */

/* Manuel wakeup source oluştur */
struct wakeup_source *ws;
ws = wakeup_source_register(NULL, "my-wakeup-source");
__pm_stay_awake(ws);   /* wakeup işlemi devam ederken */
__pm_relax(ws);        /* wakeup tamamlandığında */
wakeup_source_unregister(ws);

sysfs wakeup source yönetimi

bash
# tüm wakeup source'ları listele
cat /sys/kernel/debug/wakeup_sources
# name            active_count  event_count  wakeup_count  expire_count
# rtc0                    1           42           42             0
# gpio-keys               0            5            5             0

# bir cihazın wakeup'ını etkinleştir
cat /sys/bus/platform/devices/rtc0/power/wakeup
# enabled

echo enabled  > /sys/bus/platform/devices/soc:gpio-wakeup/power/wakeup
echo disabled > /sys/bus/platform/devices/soc:gpio-wakeup/power/wakeup

# wakeup count — suspend öncesi ve sonrası karşılaştır (race condition önleme)
cat /sys/power/wakeup_count
# 7

# wakeup_count ile yarış koşulsuz suspend
WC=$(cat /sys/power/wakeup_count)
echo "$WC" > /sys/power/wakeup_count && echo mem > /sys/power/state

RTC alarm wakeup

bash — 60 saniye sonra uyan
# rtcwake ile belirli süre sonra uyanma
rtcwake -m mem -s 60
# Sistem 60 saniye sonra otomatik uyanacak

# belirli bir saat için
rtcwake -m mem -t $(date +%s -d "tomorrow 07:00")

# test: sistemi uyutmadan sadece RTC alarm kur
rtcwake -m no -s 30   # alarm kur, uyutma
cat /proc/driver/rtc  # alarm time'ı kontrol et

03 pm_test — suspend adımlarını test et

pm_test, gerçekten uyumadan suspend/resume sırasını kısmen ya da tamamen simüle etmeni sağlar. Hangi sürücünün başarısız olduğunu bulmak için temel araçtır.

bash
# mevcut pm_test seçenekleri
cat /sys/power/pm_test
# [none] core processors platform devices freezer

# pm_test seviyeleri (her biri öncekini içerir):
# none      - normal suspend (test yok)
# core      - syscore suspend'e kadar git, geri dön
# processors- CPU suspend dahil
# platform  - platform firmware suspend dahil
# devices   - tüm cihaz driver'larını test et
# freezer   - process freeze'i test et

# devices seviyesinde test: driver'ları suspend et, uyanma olmadan dön
echo devices > /sys/power/pm_test
echo mem > /sys/power/state
# Sistem 5 saniye bekler, otomatik geri döner
# Başarısız driver dmesg'e yazılır

# testi bitir, normal moda dön
echo none > /sys/power/pm_test

# dmesg'de hata ara
dmesg | grep -E "(suspend|resume|PM:|failed)" | tail -30
KULLANIM SENARYOSU

Gerçek echo mem başarısız oluyorsa önce pm_test=freezer dene, sorun yoksa devices'a yükselt. Bu şekilde sorunlu katmanı izole edersin. Çoğu zaman bir USB host controller veya ağ sürücüsünün eksik .suspend() callback'i suçludur.

04 Driver suspend/resume hook'ları

Her platform driver, struct dev_pm_ops yapısı üzerinden suspend/resume callback'lerini kaydeder. Bu callback'lerin doğru sırayla çalışması tüm suspend/resume süreci için kritiktir.

dev_pm_ops callback'leri

c — platform driver pm ops
#include <linux/platform_device.h>
#include <linux/pm.h>

static int mydev_suspend(struct device *dev)
{
    struct mydev_priv *priv = dev_get_drvdata(dev);

    /* donanımı uyku moduna al */
    mydev_hw_suspend(priv);

    /* wakeup etkin ise interrupt kaynağını koru */
    if (device_may_wakeup(dev))
        enable_irq_wake(priv->irq);

    dev_dbg(dev, "suspended\n");
    return 0;
}

static int mydev_resume(struct device *dev)
{
    struct mydev_priv *priv = dev_get_drvdata(dev);

    if (device_may_wakeup(dev))
        disable_irq_wake(priv->irq);

    /* donanımı yeniden başlat */
    mydev_hw_resume(priv);

    dev_dbg(dev, "resumed\n");
    return 0;
}

/* noirq hook: interrupt'lar devre dışıyken çalışır */
static int mydev_suspend_noirq(struct device *dev)
{
    /* interrupt gerektirmeyen son temizlik işlemleri */
    return 0;
}

static const struct dev_pm_ops mydev_pm_ops = {
    .suspend         = mydev_suspend,
    .resume          = mydev_resume,
    .suspend_noirq   = mydev_suspend_noirq,
    .resume_noirq    = mydev_resume_noirq,
    /* Runtime PM için */
    .runtime_suspend = mydev_runtime_suspend,
    .runtime_resume  = mydev_runtime_resume,
};

static struct platform_driver mydev_driver = {
    .driver = {
        .name = "mydev",
        .pm   = &mydev_pm_ops,
    },
    .probe  = mydev_probe,
    .remove = mydev_remove,
};

Power domain

dts — power domain kullanımı
/* Power domain tanımı */
pd_vpu: power-domain@0 {
    compatible = "fsl,imx8m-blk-ctrl";
    #power-domain-cells = <1>;
};

/* Cihaz, power domain'e bağlı */
vpu: vpu@38300000 {
    compatible = "nxp,imx8mq-vpu";
    power-domains = <&pd_vpu 0>;
    /* Suspend sırasında PM core bu domain'i otomatik kapatır */
};
SIMPLE_PM_BUS

Birçok gömülü SoC'ta simple-pm-bus driver'ı, clock ve power domain yönetimini otomatik yapar. Driver yazarken bu mekanizmayı kullanmak, manuel suspend/resume yerine daha az hata içerir.

05 Freezer — process dondurma

Suspend öncesinde tüm kullanıcı alanı süreçleri ve çoğu kernel thread dondurulur. Bu adım, suspend sırasında I/O'ları yarım bırakmaktan kaynaklanan tutarsızlıkları önler.

Freeze mekanizması

c — dondurulabilir kernel thread
#include <linux/freezer.h>
#include <linux/kthread.h>

static int my_worker_thread(void *data)
{
    /* Thread'i dondurulabilir yap */
    set_freezable();

    while (!kthread_should_stop()) {
        /* Freeze isteği gelirse burada donur */
        try_to_freeze();

        /* ... asıl iş ... */

        msleep_interruptible(100);
    }
    return 0;
}

Freeze sorunlarını tespit et

bash
# hangi süreçler dondurulamıyor? (suspend başarısız mesajı)
dmesg | grep -i "freezing of tasks"
# PM: Freezing user space processes
# PM: Freezing of user space processes failed (elapsed 20.003 seconds)

# dondurulamamış süreçleri bul
dmesg | grep "Task refused to freeze"
# Task refused to freeze (pid:1234, name:myapp)

# process'in freeze durumunu kontrol et (/proc/PID/status)
grep "^Flags" /proc/1234/status
# Flags: 00400002  (PF_NOFREEZE bayrağı set ise dondurulamaz)

# dynamic debug ile freeze istatistiklerini izle
echo "file kernel/freezer.c +p" > /sys/kernel/debug/dynamic_debug/control
DİKKAT

Kullanıcı alanı süreci freeze sinyaline 20 saniye içinde yanıt vermezse suspend iptal edilir. Uzun süre bloklanan sistem çağrılarında olan D-state (uninterruptible sleep) süreçler bu duruma yol açabilir. Uygulamada SA_RESTART olmayan signal handler'lar veya yanlış konfigüre edilmiş NFS mount'ları yaygın sebeplerdir.

06 Platform-specific: i.MX6, RPi, ACPI S3

Her platform suspend/resume için farklı firmware/hardware desteği gerektirir. Üç yaygın platform için özel notlar.

i.MX6 — SNVS ile RTC wakeup

dts — i.MX6 SNVS
/* i.MX6 Secure Non-Volatile Storage — RTC ve wakeup kontrolü */
snvs: snvs@020cc000 {
    compatible = "fsl,sec-v4.0-mon", "syscon", "simple-mfd";

    snvs_rtc: snvs-rtc-lp {
        compatible = "fsl,sec-v4.0-mon-rtc-lp";
        regmap = <&snvs>;
        offset = <0x34>;
        clocks = <&clks IMX6QDL_CLK_SNVS_LP>;
        clock-names = "snvs-rtc";
        interrupts = <GIC_SPI 19 IRQ_TYPE_LEVEL_HIGH>;
    };

    snvs_poweroff: snvs-poweroff {
        compatible = "fsl,sec-v4.0-poweroff";
        regmap = <&snvs>;
    };
};
bash — i.MX6 suspend testi
# i.MX6'da S2RAM için "mem" = "deep"
echo deep > /sys/power/mem_sleep

# RTC alarm 30 saniye sonraya ayarla
rtcwake -d rtc0 -m mem -s 30

# Wakeup kaynağını kontrol et
dmesg | grep -i "wakeup\|snvs"

Raspberry Pi — PMIC wakeup

bash — Raspberry Pi suspend
# RPi 4/5'te suspend desteği (kernel 6.1+)
cat /sys/power/state
# freeze mem

# GPIO wakeup etkinleştir (örn. GPIO3 = pin 5)
echo enabled > /sys/class/gpio/gpio3/power/wakeup

# ya da /boot/config.txt ile:
# dtparam=wakeup-gpio=3

# suspend
echo mem > /sys/power/state
# GPIO3'ü GND'ye çekmek sistemi uyandırır

# RPi 5'te suspend LED göstergesi
echo heartbeat > /sys/class/leds/PWR/trigger  # uyku sırasında yanıp söner

ACPI S3 (x86/x86-64)

bash — ACPI S3
# ACPI S3 desteğini kontrol et
cat /sys/power/state
# freeze mem disk  (mem = S3)

# S3 destekleniyor mu? (ACPI tablo)
dmesg | grep -i "ACPI.*S3\|suspend.*s3"

# USB wakeup'ı yönet
for usb in /sys/bus/usb/devices/*/power/wakeup; do
    echo disabled > "$usb"  # USB wakeup'ı devre dışı bırak
done

# ağ kartının wakeup'ını aç (Wake-on-LAN)
ethtool -s eth0 wol g
echo enabled > /sys/class/net/eth0/device/power/wakeup

07 Hata ayıklama — suspend başarısız olunca

Suspend başarısız olduğunda sistematik bir yaklaşım gerekir. En yaygın sebep: eksik/hatalı driver suspend callback'i veya dondurulamamış bir süreç.

Adım adım hata ayıklama

bash — sistematik debug
# 1. dmesg'de suspend hatası ara
dmesg | grep -E "(PM:|suspend|resume|failed|error)" | tail -50

# Örnek hata çıktısı:
# PM: Suspending system (mem)
# PM: suspend of devices failed
# PM: Some devices failed to suspend, or early wake event detected
# e1000e 0000:00:19.0: Device driver failed to support suspend

# 2. pm_test ile driver isolation
echo devices > /sys/power/pm_test
echo mem > /sys/power/state
dmesg | grep -E "(PM:|failed)" | tail -20
echo none > /sys/power/pm_test

# 3. dynamic_debug ile suspend callback izle
echo "file drivers/base/power/main.c +p" \
  > /sys/kernel/debug/dynamic_debug/control
echo "file drivers/base/power/wakeup.c +p" \
  >> /sys/kernel/debug/dynamic_debug/control

# 4. Belirli bir cihazı debug et
echo "module e1000e +p" > /sys/kernel/debug/dynamic_debug/control

# 5. suspend_stats — suspend istatistikleri
cat /sys/kernel/debug/suspend_stats
# success: 15
# fail: 3
# failed_freeze: 0
# failed_prepare: 0
# failed_suspend: 3
# failed_suspend_noirq: 0
# failed_resume: 0
# last_failed_dev: e1000e

Wakeup kaynaklarını izole et

bash
# Sistem anında uyanıyorsa (spurious wakeup)
# wakeup_count yöntemini kullan
cat /sys/power/wakeup_count
# 42

echo 42 > /sys/power/wakeup_count
echo $?
# 0: başarılı (wakeup event yok, devam et)
# yazma hatası: yeni wakeup event geldi, suspend iptal

# tüm wakeup source'ları debug
cat /sys/kernel/debug/wakeup_sources | sort -k3 -rn | head -10

08 Pratik: RTC DS3231 ve GPIO button wakeup

Raspberry Pi üzerinde iki wakeup senaryosu: DS3231 RTC modülü ile zamanlı uyanma ve GPIO button ile manuel uyanma.

1 — DS3231 RTC ile Wakeup

dts — DS3231 I2C RTC overlay
/* /boot/overlays/i2c-rtc.dts benzeri */
&i2c1 {
    status = "okay";

    rtc: ds3231@68 {
        compatible = "maxim,ds3231";
        reg = <0x68>;
        /* SQW/INT pinini GPIO wakeup olarak kullan */
        interrupt-parent = <&gpio>;
        interrupts = <17 IRQ_TYPE_EDGE_FALLING>;
    };
};
bash — DS3231 ile suspend/wakeup
# DS3231 /dev/rtc1 olarak görünür (RPi'nin kendi RTC'si rtc0)
ls -la /dev/rtc*
# crw------- 1 root root 253, 0 /dev/rtc0
# crw------- 1 root root 253, 1 /dev/rtc1

# DS3231'i sistem RTC'si yap
hwclock --hctosys --rtc=/dev/rtc1

# 2 dakika sonra uyan (mem = S2RAM)
rtcwake -d /dev/rtc1 -m mem -s 120

# uyanma sonrası log
dmesg | tail -20
# PM: resume of devices complete after 423.127 msecs
# rtc-ds3231 1-0068: setting system clock to 2026-04-12T09:30:05

# periyodik wakeup scripti (her 5 dakikada bir)
cat <<'EOF' > /usr/local/bin/periodic_wake.sh
#!/bin/bash
INTERVAL=300  # saniye
while true; do
    # iş yap
    /usr/local/bin/collect_sensor_data.sh
    # uyku
    rtcwake -d /dev/rtc1 -m mem -s $INTERVAL
done
EOF
chmod +x /usr/local/bin/periodic_wake.sh

2 — GPIO Button ile Wakeup

dts — gpio-keys wakeup
/* /boot/overlays/ altında gpio-wakeup.dts */
/ {
    gpio_wakeup: gpio-keys-wakeup {
        compatible = "gpio-keys";

        wakeup-button {
            label = "Wakeup Button";
            gpios = <&gpio 3 GPIO_ACTIVE_LOW>;  /* GPIO3 = Pin 5 */
            linux,code = <KEY_WAKEUP>;
            wakeup-source;   /* bu butonu wakeup source yap */
            debounce-interval = <50>;
        };
    };
};
bash — GPIO button wakeup testi
# gpio-keys cihazının wakeup etkin olduğunu kontrol et
cat /sys/devices/platform/gpio-keys-wakeup/power/wakeup
# enabled

# suspend'e gir (GPIO3'e GND bağlamak sistemi uyandırır)
echo mem > /sys/power/state

# uyanma kaynağını öğren
dmesg | grep -i "wakeup\|gpio"
# gpio-keys: wakeup key event (KEY_WAKEUP)

3 — Suspend sonrası periferal yeniden başlatma

bash — resume hook scripti
# /lib/systemd/system-sleep/ altına script koy
cat <<'EOF' > /lib/systemd/system-sleep/reinit-peripherals.sh
#!/bin/bash
case "$1" in
  pre)
    echo "Suspend öncesi: sensörleri durdur"
    systemctl stop sensor-daemon.service
    ;;
  post)
    echo "Resume sonrası: sensörleri yeniden başlat"
    # I2C sensör yeniden başlat
    i2cset -y 1 0x76 0xF3 0xFE  # BME280 reset
    sleep 0.1
    systemctl start sensor-daemon.service
    # GPS seri portu yeniden aç
    systemctl restart gpsd.service
    ;;
esac
EOF
chmod +x /lib/systemd/system-sleep/reinit-peripherals.sh
SONUÇ

RTC tabanlı periyodik wakeup, pil ömrü kritik IoT sensör düğümlerinde en yaygın kullanılan senaryodur. Raspberry Pi + DS3231 kombinasyonunda uyku sırasında tüketim ~2 mA seviyesine inerken, aktif ölçüm anında ~500 mA'ye çıkabilir. %5 aktif süre hedeflendiğinde ortalama tüketim ~27 mA olur.