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:
CPUfreq ile karşılaştırma
| Özellik | CPUfreq | devfreq |
|---|---|---|
| Hedef donanım | Yalnızca CPU çekirdekleri | GPU, DDR, interconnect, ISP, DSP |
| Governor tipleri | ondemand, schedutil, conservative | simple_ondemand, powersave, performance, passive, userspace |
| Yük ölçümü | idle time, scheduler stats | get_dev_status() callback — cihaza özgü |
| OPP entegrasyonu | cpufreq_driver OPP tablosu | dev_pm_opp_* API'si |
| Thermal cooling | cpufreq_cooling_register | devfreq_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
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ı
| Governor | Davranış | Güç tasarrufu | Kullanım alanı |
|---|---|---|---|
| simple_ondemand | Yük tabanlı dinamik | İyi (%30–50) | GPU, genel amaçlı |
| powersave | Daima minimum | Maksimum | Pil tasarrufu modu |
| performance | Daima maksimum | Yok | Benchmark, geliştirme |
| userspace | Kullanıcı kontrolü | Kullanıcıya bağlı | Özel güç yönetimi, test |
| passive | Parent cihaza bağlı | İyi | DDR/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.c | Mali Midgard/Bifrost (T600, T700, G31–G76) |
| drivers/gpu/drm/lima/lima_devfreq.c | Mali 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
| Belirti | Neden | Çözüm |
|---|---|---|
| cur_freq hiç değişmiyor | Governor donmuş, get_dev_status hatalı | polling_ms kontrol et; dmesg'e bak; governor değiştir |
| devfreq_add_device -EINVAL | OPP tablosu boş veya profil eksik | dev_pm_opp_get_opp_count() pozitif olduğunu doğrula |
| Frekans maksimumda takılı | performance governor veya thermal cooling | governor ve thermal cur_state kontrol et |
| Yüksek güç tüketimi | performance governor aktif | simple_ondemand'e geç; upthreshold düşür |
| OPP tablosu DT'den yüklenmiyor | of_dev_pm_opp_add_table hatası | DT compatible ve phandle bağlantısını kontrol et |
| Thermal kısıtlama çalışmıyor | cooling device kaydı eksik | devfreq_cooling_register() çağrısını ve thermal zone trip binding'i doğrula |