Tüm eğitimler
TEKNİK REHBER GÖMÜLÜ LİNUX GÜÇ YÖNETİMİ 2026

devfreq
CPU Dışı Cihaz Dinamik Frekans Ölçekleme

GPU, DDR bellek ve interconnect için Linux güç yönetimi çerçevesi — OPP tablosu, governor ve thermal entegrasyonu.

00 devfreq neden var

Modern SoC'larda CPU dışında da frekans/gerilim ölçeklemesi gereken çok sayıda bileşen bulunur; devfreq bu bileşenler için CPUfreq'e benzer bir çerçeve sağlar.

CPU dışı DVFS ihtiyacı

DVFS (Dynamic Voltage and Frequency Scaling) yalnızca işlemci için değil, pek çok SoC bileşeni için kritiktir:

GPU3D işlemci, video kod çözücü ve görüntü işleme birimi — yük ile orantılı frekans ve gerilim değişimi güç tüketimini %40–60 azaltabilir
DDR bellekBellek frekansı, bant genişliği ve güç tüketimini doğrudan etkiler; düşük CPU yükünde DDR frekansını düşürmek önemli enerji tasarrufu sağlar
Interconnect (NoC/fabric)SoC iç veri yollarının bant genişliği; yüksek DDR transferleri gerektiren durumlarda frekans artırılmalı, boşta düşürülmeli
ISP / DSPGörüntü işleme ve sinyal işleme bloklarının kamera çözünürlüğüne ve kare hızına göre dinamik ölçekleme ihtiyacı
USB / PCIe PHYYüksek hız arabirimlerinin güç durumları, bağlantı hızı ile orantılı frekans gereksinimleri

CPUfreq ile karşılaştırma

ÖzellikCPUfreqdevfreq
Hedef donanımYalnızca CPU çekirdekleriGPU, DDR, interconnect, ISP, DSP
Governor tipleriondemand, schedutil, conservativesimple_ondemand, powersave, performance, passive, userspace
Yük ölçümüidle time, scheduler statsget_dev_status() callback — cihaza özgü
OPP entegrasyonucpufreq_driver OPP tablosudev_pm_opp_* API'si
Thermal coolingcpufreq_cooling_registerdevfreq_cooling_register
Çoklu cihaz bağlantısıYok (her CPU bağımsız)passive governor — parent/child ilişkisi
Sysfs arayüzü/sys/devices/system/cpu/cpufreq//sys/class/devfreq/

Güç tasarrufu potansiyeli

Boştaki bir SoC'nin güç profili (örnek: Exynos 5422):
  CPU kümesi:    ~180 mW  (CPUfreq ile ölçekleme: ~50 mW)
  Mali-T628 GPU: ~120 mW  (devfreq ile ölçekleme: ~20 mW)
  DDR3 bellek:   ~90 mW   (devfreq ile ölçekleme: ~35 mW)
  Interconnect:  ~40 mW   (devfreq ile ölçekleme: ~15 mW)

  devfreq + CPUfreq birlikte: toplam güç ~%45 azalır
    

01 devfreq framework mimarisi

devfreq çerçevesi üç temel bileşenden oluşur: cihaz profili (devfreq_dev_profile), governor ve OPP tablosu.

Yazılım katman diyagramı

Kullanıcı alanı
  /sys/class/devfreq/<dev>/
        │ sysfs okuma/yazma
        ▼
┌───────────────────────────────────┐
│         devfreq core              │
│  devfreq_add_device()             │
│  devfreq_update_stats()           │
│  devfreq_monitor_start()          │
├───────────────────────────────────┤
│         Governor katmanı          │
│  simple_ondemand / passive /      │
│  powersave / performance          │
│  devfreq_governor.get_target_freq │
├───────────────────────────────────┤
│         OPP tablosu               │
│  dev_pm_opp_add() / DT opp-table  │
│  dev_pm_opp_get_opp_count()       │
├───────────────────────────────────┤
│         Cihaz sürücüsü            │
│  devfreq_dev_profile.target()     │
│  devfreq_dev_profile.get_dev_status│
└───────────────────────────────────┘
        │
        ▼
  Donanım (CLK, regulator çerçevesi)
  clk_set_rate() + regulator_set_voltage()
    

devfreq_dev_profile yapısı

#include <linux/devfreq.h>

/*
 * devfreq_dev_profile — sürücünün devfreq core'a
 * sağladığı callback'ler ve parametreler
 */
struct devfreq_dev_profile {
    unsigned long   initial_freq;   /* başlangıç frekansı */
    unsigned int    polling_ms;     /* governor polling aralığı (ms) */

    /* Frekans ayarla — sürücü implement eder */
    int (*target)(struct device *dev,
                  unsigned long *freq,
                  u32 flags);

    /* Mevcut yük istatistikleri — governor okur */
    int (*get_dev_status)(struct device *dev,
                          struct devfreq_dev_status *stat);

    /* Mevcut frekansı oku */
    int (*get_cur_freq)(struct device *dev,
                        unsigned long *freq);

    /* Kullanılabilir frekans tablosu */
    unsigned long   *freq_table;
    unsigned int    max_state;
};

devfreq_dev_status: yük ölçümü

struct devfreq_dev_status {
    unsigned long   total_time;    /* ölçüm penceresi (µs veya cycle) */
    unsigned long   busy_time;     /* meşgul geçen süre */
    unsigned long   current_frequency; /* şu anki frekans */
    void            *private_data; /* governor'a özel veri */
};

/* Yük yüzdesi: busy_time / total_time * 100 */

devfreq lifecycle

platform_driver probe()
      │
      ├─ OPP tablosunu oluştur (DT veya kod)
      ├─ devfreq_dev_profile doldur
      │
      ▼
devfreq_add_device()
      │
      ├─ Governor seç (ör. simple_ondemand)
      ├─ devfreq_monitor_start() — periyodik polling timer
      │
      ▼
Governor polling (polling_ms aralığıyla)
      │
      ├─ get_dev_status() çağır
      ├─ Yük hesapla
      ├─ Hedef frekans seç (OPP tablosundan)
      │
      ▼
profile->target(dev, &freq, flags)
      │
      ├─ clk_set_rate()
      └─ regulator_set_voltage()
    

02 OPP (Operating Performance Point)

OPP tablosu, bir cihazın desteklediği frekans-gerilim çiftlerini tanımlar; Device Tree binding veya kod aracılığıyla oluşturulabilir.

Device Tree OPP tablosu

/* arch/arm64/boot/dts/vendor/soc.dtsi */

gpu: gpu@13000000 {
    compatible = "arm,mali-t628";
    reg = <0x13000000 0x4000>;
    clocks = <&clock CLK_GPU>;
    clock-names = "clk_g3d";

    operating-points-v2 = <&gpu_opp_table>;

    devfreq,min-freq = <160000000>;
    devfreq,max-freq = <600000000>;
};

gpu_opp_table: opp-table-gpu {
    compatible = "operating-points-v2";
    opp-shared;

    opp-160000000 {
        opp-hz = /bits/ 64 <160000000>;
        opp-microvolt = <800000>;
    };
    opp-266000000 {
        opp-hz = /bits/ 64 <266000000>;
        opp-microvolt = <875000>;
    };
    opp-350000000 {
        opp-hz = /bits/ 64 <350000000>;
        opp-microvolt = <912500>;
    };
    opp-480000000 {
        opp-hz = /bits/ 64 <480000000>;
        opp-microvolt = <962500>;
    };
    opp-600000000 {
        opp-hz = /bits/ 64 <600000000>;
        opp-microvolt = <1000000>;
    };
};

Kod ile OPP ekleme

#include <linux/pm_opp.h>

static const struct {
    unsigned long freq;
    unsigned long volt;
} gpu_opps[] = {
    { 160000000, 800000  },
    { 266000000, 875000  },
    { 350000000, 912500  },
    { 480000000, 962500  },
    { 600000000, 1000000 },
};

static int gpu_opp_init(struct device *dev)
{
    int i, ret;

    for (i = 0; i < ARRAY_SIZE(gpu_opps); i++) {
        ret = dev_pm_opp_add(dev,
                             gpu_opps[i].freq,
                             gpu_opps[i].volt);
        if (ret) {
            dev_err(dev, "OPP eklenemedi %lu Hz: %d\n",
                    gpu_opps[i].freq, ret);
            goto err_remove;
        }
    }
    return 0;

err_remove:
    dev_pm_opp_remove_all_dynamic(dev);
    return ret;
}

OPP çerçevesi API'si

dev_pm_opp_add(dev, freq, volt)Dinamik OPP ekler; Device Tree OPP'leri of_dev_pm_opp_add() ile otomatik eklenir
dev_pm_opp_remove(dev, freq)Belirli frekansa ait OPP'yi kaldırır
dev_pm_opp_get_opp_count(dev)Etkin OPP sayısını döner
dev_pm_opp_find_freq_ceil(dev, freq)İstenen frekansın üstündeki en yakın OPP'yi bulur (yuvarlama yukarı)
dev_pm_opp_find_freq_floor(dev, freq)İstenen frekansın altındaki en yakın OPP'yi bulur (yuvarlama aşağı)
dev_pm_opp_set_rate(dev, target_freq)OPP tablosundan frekans seçer, clk_set_rate + regulator_set_voltage çağırır

OPP notifier

/* Frekans değişimlerini dinle */
static int gpu_opp_notifier(struct notifier_block *nb,
                             unsigned long event, void *data)
{
    struct dev_pm_opp_notifier_block *opp_nb =
        container_of(nb, struct dev_pm_opp_notifier_block, nb);
    struct dev_pm_opp *opp = data;

    if (event == OPP_EVENT_ENABLE)
        dev_dbg(opp_nb->dev, "OPP etkinleşti: %lu Hz\n",
                dev_pm_opp_get_freq(opp));
    return NOTIFY_OK;
}

struct dev_pm_opp_notifier_block gpu_opp_nb = {
    .nb.notifier_call = gpu_opp_notifier,
};

dev_pm_opp_register_notifier(dev, &gpu_opp_nb.nb);

03 Yerleşik governor'lar

devfreq framework dört yerleşik governor içerir; her biri farklı güç-performans dengesi için tasarlanmıştır.

simple_ondemand

En yaygın kullanılan governor. Cihazın yüklenmesine bakarak frekansı dinamik olarak ayarlar. Yük eşiği aşılınca maksimum frekansa çıkar, düşünce kademeli olarak azaltır.

/* simple_ondemand parametreleri */
struct devfreq_simple_ondemand_data {
    unsigned int upthreshold;      /* varsayılan %90 */
    unsigned int downdifferential; /* varsayılan %5  */
};

static struct devfreq_simple_ondemand_data gpu_gov_data = {
    .upthreshold      = 85,
    .downdifferential = 10,
};

struct devfreq *df = devfreq_add_device(dev,
                                         &gpu_profile,
                                         DEVFREQ_GOV_SIMPLE_ONDEMAND,
                                         &gpu_gov_data);

powersave ve performance

/* powersave: daima minimum OPP */
struct devfreq *df_ps = devfreq_add_device(dev, &profile,
                                            DEVFREQ_GOV_POWERSAVE, NULL);

/* performance: daima maksimum OPP */
struct devfreq *df_pf = devfreq_add_device(dev, &profile,
                                            DEVFREQ_GOV_PERFORMANCE, NULL);

/* Çalışma zamanında governor değiştirme (sysfs) */
/* echo powersave > /sys/class/devfreq/13000000.gpu/governor */

userspace governor

/* Kullanıcı alanından frekans kontrolü */
struct devfreq *df = devfreq_add_device(dev, &profile,
                                         DEVFREQ_GOV_USERSPACE, NULL);

/* Kullanıcı alanında:
 * echo 350000000 > /sys/class/devfreq/13000000.gpu/userspace/set_freq
 */

Governor karşılaştırması

GovernorDavranışGüç tasarrufuKullanım alanı
simple_ondemandYük tabanlı dinamikİyi (%30–50)GPU, genel amaçlı
powersaveDaima minimumMaksimumPil tasarrufu modu
performanceDaima maksimumYokBenchmark, geliştirme
userspaceKullanıcı kontrolüKullanıcıya bağlıÖzel güç yönetimi, test
passiveParent cihaza bağlıİyiDDR/interconnect, CPU'ya bağımlı

Governor sysfs arayüzü

# Mevcut governor
cat /sys/class/devfreq/13000000.gpu/governor
# simple_ondemand

# Governor değiştir
echo performance > /sys/class/devfreq/13000000.gpu/governor

# ondemand parametrelerini ayarla
echo 80 > /sys/class/devfreq/13000000.gpu/upthreshold

# Polling aralığı (ms)
echo 100 > /sys/class/devfreq/13000000.gpu/polling_interval

04 devfreq sürücüsü yazımı

Özel bir SoC bileşeni için devfreq sürücüsü yazmak; target callback, get_dev_status ve OPP entegrasyonunu içerir.

Tam devfreq sürücüsü iskelet

#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/devfreq.h>
#include <linux/pm_opp.h>
#include <linux/clk.h>
#include <linux/regulator/consumer.h>

struct my_devfreq_data {
    struct device       *dev;
    struct devfreq      *devfreq;
    struct clk          *clk;
    struct regulator    *vdd;
    void __iomem        *perf_regs;
    u64                  last_total;
    u64                  last_busy;
};

static void read_hw_counters(struct my_devfreq_data *priv,
                              u64 *total, u64 *busy)
{
    *total = readl(priv->perf_regs + 0x00);
    *busy  = readl(priv->perf_regs + 0x04);
    writel(0, priv->perf_regs + 0x08);
}

static int my_get_dev_status(struct device *dev,
                              struct devfreq_dev_status *stat)
{
    struct my_devfreq_data *priv = dev_get_drvdata(dev);
    u64 total, busy;

    read_hw_counters(priv, &total, &busy);

    stat->current_frequency = clk_get_rate(priv->clk);
    stat->total_time = total - priv->last_total;
    stat->busy_time  = busy  - priv->last_busy;

    priv->last_total = total;
    priv->last_busy  = busy;

    return 0;
}

static int my_target(struct device *dev,
                     unsigned long *freq,
                     u32 flags)
{
    struct my_devfreq_data *priv = dev_get_drvdata(dev);
    struct dev_pm_opp *opp;
    unsigned long target_volt;
    int ret;

    opp = devfreq_recommended_opp(dev, freq, flags);
    if (IS_ERR(opp)) return PTR_ERR(opp);

    target_volt = dev_pm_opp_get_voltage(opp);
    dev_pm_opp_put(opp);

    if (*freq > clk_get_rate(priv->clk)) {
        ret = regulator_set_voltage(priv->vdd,
                                    target_volt, target_volt);
        if (ret) return ret;
    }

    ret = clk_set_rate(priv->clk, *freq);
    if (ret) return ret;

    if (*freq < clk_get_rate(priv->clk))
        regulator_set_voltage(priv->vdd, target_volt, target_volt);

    return 0;
}

static int my_get_cur_freq(struct device *dev, unsigned long *freq)
{
    struct my_devfreq_data *priv = dev_get_drvdata(dev);
    *freq = clk_get_rate(priv->clk);
    return 0;
}

static struct devfreq_dev_profile my_devfreq_profile = {
    .initial_freq   = 160000000,
    .polling_ms     = 50,
    .target         = my_target,
    .get_dev_status = my_get_dev_status,
    .get_cur_freq   = my_get_cur_freq,
};

static int my_devfreq_probe(struct platform_device *pdev)
{
    struct device *dev = &pdev->dev;
    struct my_devfreq_data *priv;
    struct devfreq_simple_ondemand_data *gov_data;
    int ret;

    priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
    if (!priv) return -ENOMEM;
    priv->dev = dev;

    priv->clk = devm_clk_get(dev, "core");
    if (IS_ERR(priv->clk)) return PTR_ERR(priv->clk);

    priv->vdd = devm_regulator_get(dev, "vdd-core");
    if (IS_ERR(priv->vdd)) return PTR_ERR(priv->vdd);

    priv->perf_regs = devm_platform_ioremap_resource(pdev, 1);
    if (IS_ERR(priv->perf_regs)) return PTR_ERR(priv->perf_regs);

    ret = devm_pm_opp_of_add_table(dev);
    if (ret) {
        dev_err(dev, "OPP tablosu yüklenemedi: %d\n", ret);
        return ret;
    }

    dev_set_drvdata(dev, priv);

    gov_data = devm_kzalloc(dev, sizeof(*gov_data), GFP_KERNEL);
    gov_data->upthreshold = 85;
    gov_data->downdifferential = 10;

    priv->devfreq = devm_devfreq_add_device(dev,
                                              &my_devfreq_profile,
                                              DEVFREQ_GOV_SIMPLE_ONDEMAND,
                                              gov_data);
    if (IS_ERR(priv->devfreq)) {
        dev_err(dev, "devfreq eklenemedi\n");
        return PTR_ERR(priv->devfreq);
    }

    dev_info(dev, "devfreq başlatıldı: %lu Hz\n",
             clk_get_rate(priv->clk));
    return 0;
}

static const struct of_device_id my_devfreq_of_match[] = {
    { .compatible = "vendor,my-gpu" },
    { }
};

static struct platform_driver my_devfreq_driver = {
    .probe  = my_devfreq_probe,
    .driver = {
        .name           = "my-devfreq",
        .of_match_table = my_devfreq_of_match,
    },
};
module_platform_driver(my_devfreq_driver);
MODULE_LICENSE("GPL");

05 Passive governor

Passive governor, bir devfreq cihazının frekansını başka bir devfreq cihazına (parent) veya CPUfreq'e bağlar; DDR ve interconnect gibi CPU'ya bağımlı bileşenler için kullanılır.

Passive governor kavramı

DDR frekansı CPU frekansına yakından bağlıdır: düşük CPU frekansında yüksek DDR frekansı anlamsız güç tüketimidir, yüksek CPU frekansında ise düşük DDR frekansı darboğaz oluşturur.

CPUfreq (parent)              DDR devfreq (child)
  CPU: 400 MHz    ───────────→   DDR: 400 MHz
  CPU: 800 MHz    ───────────→   DDR: 800 MHz
  CPU: 1.6 GHz    ───────────→   DDR: 1600 MHz

  Parent frekans değişimi → passive governor → child frekans güncellenir
    

devfreq_passive_data yapısı

#include <linux/devfreq-event.h>

struct devfreq_passive_data {
    struct devfreq  *parent;    /* NULL = CPUfreq'i dinle */

    int (*get_target_freq)(struct devfreq *devfreq,
                           unsigned long *freq);
};

/* DDR devfreq sürücüsünde: CPUfreq'e pasif bağlanma */
static struct devfreq_passive_data ddr_passive_data = {
    .parent = NULL,
};

struct devfreq *ddr_df = devfreq_add_device(dev,
                                             &ddr_profile,
                                             DEVFREQ_GOV_PASSIVE,
                                             &ddr_passive_data);

Parent-child frekans eşleme tablosu (DT)

/* arch/arm64/boot/dts/vendor/board.dts */

dmc: dmc@10000000 {
    compatible = "vendor,ddr-ctrl";
    operating-points-v2 = <&ddr_opp_table>;
    devfreq-events = <&ddr_perf>;
};

ddr_opp_table: opp-table-ddr {
    compatible = "operating-points-v2";

    opp-400000000 {
        opp-hz = /bits/ 64 <400000000>;
        opp-microvolt = <1100000>;
        required-opps = <&cpu_opp_400mhz>;
    };
    opp-800000000 {
        opp-hz = /bits/ 64 <800000000>;
        opp-microvolt = <1150000>;
        required-opps = <&cpu_opp_800mhz>;
    };
    opp-1600000000 {
        opp-hz = /bits/ 64 <1600000000>;
        opp-microvolt = <1200000>;
        required-opps = <&cpu_opp_1600mhz>;
    };
};

Özel frekans dönüşüm fonksiyonu

/* CPU 1 GHz → DDR 800 MHz gibi özel eşleme */
static int custom_freq_mapping(struct devfreq *devfreq,
                                unsigned long *freq)
{
    unsigned long cpu_freq = *(unsigned long *)devfreq->data;

    if (cpu_freq <= 400000000)
        *freq = 400000000;
    else if (cpu_freq <= 800000000)
        *freq = 533000000;
    else if (cpu_freq <= 1200000000)
        *freq = 800000000;
    else
        *freq = 1600000000;

    return 0;
}

static struct devfreq_passive_data custom_passive = {
    .parent          = NULL,
    .get_target_freq = custom_freq_mapping,
};

06 Thermal + devfreq entegrasyonu

devfreq cooling device, GPU veya DDR sıcaklığı belirli eşiği aşınca frekansı otomatik düşürerek termal koruma sağlar.

devfreq_cooling_register

#include <linux/devfreq_cooling.h>

struct thermal_cooling_device *cdev;

/* devfreq cihazını thermal cooling olarak kaydet */
cdev = devfreq_cooling_em_register(devfreq, em_dev);
if (IS_ERR(cdev)) {
    dev_err(dev, "thermal cooling kaydı başarısız: %ld\n",
            PTR_ERR(cdev));
    return PTR_ERR(cdev);
}

/* Energy Model olmadan (daha basit) */
cdev = devfreq_cooling_register(devfreq);

/* Temizleme */
devfreq_cooling_unregister(cdev);

Device Tree thermal zone tanımı

/* Thermal zone — GPU için */
thermal-zones {
    gpu-thermal {
        polling-delay-passive = <100>;
        polling-delay          = <1000>;

        thermal-sensors = <&gpu_tsens>;

        trips {
            gpu_alert: gpu-alert {
                temperature = <75000>;
                hysteresis  = <2000>;
                type        = "passive";
            };
            gpu_crit: gpu-crit {
                temperature = <95000>;
                hysteresis  = <5000>;
                type        = "critical";
            };
        };

        cooling-maps {
            map0 {
                trip         = <&gpu_alert>;
                cooling-device = <&gpu
                    THERMAL_NO_LIMIT
                    THERMAL_NO_LIMIT>;
            };
        };
    };
};

Thermal state eşleme

/*
 * devfreq cooling: OPP sayısı = thermal state sayısı
 * state 0 = maksimum frekans (en az kısıtlama)
 * state N = minimum frekans (en fazla kısıtlama)
 *
 * 5 OPP ile 5 thermal state örneği:
 * state 0: 600 MHz — normal
 * state 1: 480 MHz — 75°C uyarı
 * state 2: 350 MHz
 * state 3: 266 MHz
 * state 4: 160 MHz — termal acil
 */

cat /sys/class/thermal/cooling_device0/cur_state
cat /sys/class/thermal/cooling_device0/max_state

Power-aware governor ile thermal

/* devfreq + EM (Energy Model) ile güç bütçesi */
#include <linux/energy_model.h>

static int my_gpu_em_probe(struct device *dev)
{
    struct em_data_callback cb = {
        .active_power = gpu_active_power,
    };

    return em_dev_register_perf_domain(dev,
                                        gpu_opp_count,
                                        &cb, cpu_mask, false);
}

cdev = devfreq_cooling_em_register(devfreq, em_dev);
/*
 * Thermal framework sadece frekansı değil
 * gerçek güç tüketimini baz alarak kısıtlar.
 * CPU + GPU + DDR güç bütçesi birlikte yönetilir.
 */

07 SoC örnekleri

Mali GPU, Exynos DDR, Rockchip RK3399 ve i.MX8M gibi farklı SoC ailelerindeki devfreq implementasyonlarının incelenmesi.

Mali GPU devfreq — Panfrost (mainline)

Sürücü dosyasıHedef donanım
drivers/gpu/drm/panfrost/panfrost_devfreq.cMali Midgard/Bifrost (T600, T700, G31–G76)
drivers/gpu/drm/lima/lima_devfreq.cMali 400/450 (eski nesil)
/* Panfrost devfreq implementasyonu özeti */

static int panfrost_devfreq_target(struct device *dev,
                                    unsigned long *freq, u32 flags)
{
    struct panfrost_device *pfdev = dev_get_drvdata(dev);
    struct dev_pm_opp *opp;
    int err;

    opp = devfreq_recommended_opp(dev, freq, flags);
    if (IS_ERR(opp)) return PTR_ERR(opp);
    dev_pm_opp_put(opp);

    err = dev_pm_opp_set_rate(dev, *freq);
    return err;
}

static void panfrost_devfreq_get_status(
        struct panfrost_device *pfdev,
        struct devfreq_dev_status *stat)
{
    stat->current_frequency = clk_get_rate(pfdev->clock);
    panfrost_cycle_counter_get(pfdev);
    stat->busy_time  = pfdev->devfreq.busy_time;
    stat->total_time = pfdev->devfreq.total_time;
    pfdev->devfreq.busy_time  = 0;
    pfdev->devfreq.total_time = 0;
}

Exynos DDR devfreq (exynos-bus)

/* drivers/devfreq/exynos-bus.c
 *
 * Exynos PPMU (Platform Performance Monitoring Unit) donanımını
 * kullanarak gerçek bus yük ölçümü yapar.
 * PPMU okuma → yük hesabı → governor kararı
 */

/* DT yapılandırması (Samsung Exynos 5422) */
/*
bus_wcore: bus-wcore {
    compatible = "samsung,exynos-bus";
    clocks = <&clock CLK_ACLK_G2D_333>;
    clock-names = "bus";
    operating-points-v2 = <&bus_wcore_opp_table>;
    devfreq-events = <&ppmu_event0_3>;
    samsung,data-ratio = <4>;
    status = "okay";
};
*/

Rockchip RK3399 DDR devfreq

/* drivers/devfreq/rk3399_dmc.c
 *
 * RK3399: ATF (ARM Trusted Firmware) SMCCC üzerinden frekans değişimi.
 * DRAM kontrolörü EL3'te yönetilir; Linux sürücüsü ATF'ye istek gönderir.
 */

static int rk3399_dmcfreq_target(struct device *dev,
                                  unsigned long *freq, u32 flags)
{
    struct rk3399_dmcfreq *dmcfreq = dev_get_drvdata(dev);
    struct dev_pm_opp *opp;
    unsigned long target_volt, target_rate;
    int err;

    opp = devfreq_recommended_opp(dev, freq, flags);
    if (IS_ERR(opp)) return PTR_ERR(opp);
    target_rate = dev_pm_opp_get_freq(opp);
    target_volt = dev_pm_opp_get_voltage(opp);
    dev_pm_opp_put(opp);

    /* ATF SMCCC ile DDR frekansını değiştir */
    rockchip_ddr_set_auto_self_refresh(0);
    err = clk_set_rate(dmcfreq->dmc_clk, target_rate);
    rockchip_ddr_set_auto_self_refresh(1);

    return err;
}

/* DT */
/*
dmc: dmc {
    compatible = "rockchip,rk3399-dmc";
    devfreq-events = <&dfi>;
    clocks = <&cru SCLK_DDRCLK>;
    clock-names = "dmc_clk";
    operating-points-v2 = <&dmc_opp_table>;
    center-supply = <&ppvar_centerlogic>;
    rockchip,pmu = <&pmu>;
};
*/

i.MX8M DDR devfreq

/* drivers/devfreq/imx8m-ddrc.c
 *
 * i.MX8M: DDR frequency scaling via GPC + DDRC sürücüsü.
 * Frekans değişimi TCM'de çalışan firmware ile yapılır;
 * A53 çekirdekleri değişim sırasında kısa süre duraklatılır.
 *
 * CONFIG_ARM_IMX8M_DDRC_DEVFREQ=y gerektirir
 */

/* DT — i.MX8MQ */
/*
ddrc: memory-controller@3d400000 {
    compatible = "fsl,imx8mq-ddrc", "fsl,imx8m-ddrc";
    reg = <0x3d400000 0x400000>;
    clock-names = "core", "pll", "alt", "apb";
    clocks = <&clk IMX8MQ_CLK_DRAM_CORE>,
             <&clk IMX8MQ_DRAM_PLL>,
             <&clk IMX8MQ_CLK_DRAM_ALT>,
             <&clk IMX8MQ_CLK_DRAM_APB>;
    operating-points-v2 = <&ddr_opp_table>;
};
*/

08 Hata ayıklama

/sys/class/devfreq/ sysfs arayüzü, trans_stat ve OPP debugfs dosyaları devfreq sorunlarını teşhis etmek için gerekli bilgileri sağlar.

/sys/class/devfreq/ arayüzü

# Sistemdeki tüm devfreq cihazlarını listele
ls /sys/class/devfreq/
# 13000000.gpu  ff9a0000.dmc  ffe80000.interconnect

# Mevcut frekans
cat /sys/class/devfreq/13000000.gpu/cur_freq
# 350000000

# Minimum/maksimum frekans sınırları
cat /sys/class/devfreq/13000000.gpu/min_freq
cat /sys/class/devfreq/13000000.gpu/max_freq

# Mevcut governor
cat /sys/class/devfreq/13000000.gpu/governor

# Tüm kullanılabilir governor'lar
cat /sys/class/devfreq/13000000.gpu/available_governors
# simple_ondemand powersave performance userspace passive

# Kullanılabilir frekanslar (OPP tablosundan)
cat /sys/class/devfreq/13000000.gpu/available_frequencies
# 160000000 266000000 350000000 480000000 600000000

trans_stat: geçiş istatistikleri

# Frekans geçiş matrisi
cat /sys/class/devfreq/13000000.gpu/trans_stat

# Örnek çıktı:
#      From  :   To
#           :  160MHz  266MHz  350MHz  480MHz  600MHz  time(ms)
# 160000000 :       0     245      12       3       1    45231
# 266000000 :     189       0     301      45       8    12340
# 350000000 :       8     287       0     123      34     8765
# 480000000 :       1      32     108       0      87     3421
# 600000000 :       0       5      28      91       0     1234

# Trans_stat sıfırla
echo 0 > /sys/class/devfreq/13000000.gpu/trans_stat

OPP debugfs

# OPP debugfs (CONFIG_DEBUG_FS=y gerektirir)
ls /sys/kernel/debug/opp/

# Belirli cihazın OPP tablosu
cat /sys/kernel/debug/opp/13000000.gpu/opp_summary

# Örnek çıktı:
# device                rate(Hz)    volt(uV)    enabled
# 13000000.gpu         160000000     800000       true
# 13000000.gpu         266000000     875000       true
# 13000000.gpu         350000000     912500       true
# 13000000.gpu         480000000     962500       true
# 13000000.gpu         600000000    1000000       true

devfreq olaylarını izleme

# ftrace ile devfreq frekans değişimlerini takip et
echo 1 > /sys/kernel/debug/tracing/events/devfreq/devfreq_frequency/enable

cat /sys/kernel/debug/tracing/trace | grep devfreq_frequency

# Örnek çıktı:
#  kworker/1:2  [001]  1234.567: devfreq_frequency:
#      dev=13000000.gpu freq=350000000 prev=266000000

# Güç olayları
echo 1 > /sys/kernel/debug/tracing/events/power/enable

Hızlı tanı betiği

#!/bin/sh
# devfreq sistem sağlık kontrolü

echo "=== devfreq Cihazları ==="
for dev in /sys/class/devfreq/*/; do
    name=$(basename "$dev")
    cur=$(cat "$dev/cur_freq" 2>/dev/null)
    gov=$(cat "$dev/governor" 2>/dev/null)
    printf "%-30s  cur=%-12s  gov=%s\n" "$name" "$cur" "$gov"
done

echo ""
echo "=== Frekans Sınırları ==="
for dev in /sys/class/devfreq/*/; do
    name=$(basename "$dev")
    min=$(cat "$dev/min_freq" 2>/dev/null)
    max=$(cat "$dev/max_freq" 2>/dev/null)
    printf "%-30s  min=%-12s  max=%s\n" "$name" "$min" "$max"
done

echo ""
echo "=== Thermal Cooling Durumu ==="
for cdev in /sys/class/thermal/cooling_device*/; do
    type=$(cat "$cdev/type" 2>/dev/null)
    cur=$(cat "$cdev/cur_state" 2>/dev/null)
    max=$(cat "$cdev/max_state" 2>/dev/null)
    if echo "$type" | grep -qi "devfreq\|gpu\|ddr"; then
        printf "%-40s  state=%s/%s\n" "$type" "$cur" "$max"
    fi
done

Yaygın sorunlar

BelirtiNedenÇözüm
cur_freq hiç değişmiyorGovernor donmuş, get_dev_status hatalıpolling_ms kontrol et; dmesg'e bak; governor değiştir
devfreq_add_device -EINVALOPP tablosu boş veya profil eksikdev_pm_opp_get_opp_count() pozitif olduğunu doğrula
Frekans maksimumda takılıperformance governor veya thermal coolinggovernor ve thermal cur_state kontrol et
Yüksek güç tüketimiperformance governor aktifsimple_ondemand'e geç; upthreshold düşür
OPP tablosu DT'den yüklenmiyorof_dev_pm_opp_add_table hatasıDT compatible ve phandle bağlantısını kontrol et
Thermal kısıtlama çalışmıyorcooling device kaydı eksikdevfreq_cooling_register() çağrısını ve thermal zone trip binding'i doğrula