Tüm eğitimler
TEKNİK REHBER GÖMÜLÜ LİNUX REGULATOR / PMIC 2026

Regulator Framework
Linux PMIC & Güç Rayı Yönetimi

PMIC sürücüsü yazımı, consumer API, DT binding ve debugfs ile güç ray doğrulaması — eksiksiz regulator rehberi.

00 Regulator framework neden var — PMIC, LDO, DCDC

Linux Regulator Framework, SoC ve çevre birimlerinin güç raylarını merkezi, tutarlı ve güvenli biçimde yönetmek için tasarlanmış kernel alt sistemidir.

Modern embedded sistemlerde bir PMIC (Power Management IC) onlarca güç rayı üretir: işlemci çekirdeği için değişken gerilim (DVFS), bellek için sabit 1.8 V, I/O için 3.3 V, RF için 1.1 V vs. Framework olmadan her peripheral sürücüsü doğrudan PMIC register'larını manipüle etmek zorunda kalır — bu kod tekrarına, güvenlik açıklarına ve eş zamanlılık sorunlarına yol açar.

  +---------------------------------------------+
  |           Consumer Drivers                  |
  | (GPU, WiFi, Kamera, USB, DRAM controller)   |
  +----+--------+--------+--------+-------------+
       |        |        |        |
   regulator_enable / set_voltage / get_voltage
       |        |        |        |
  +----v--------v--------v--------v-------------+
  |         Regulator Framework Core            |
  |    (constraints check, ref counting,        |
  |     voltage rounding, notifiers)            |
  +----+--------+--------+--------+-------------+
       |        |        |        |
  +----v--+ +---v---+ +--v----+ +-v------+
  | LDO1  | | LDO2  | | DCDC1 | | DCDC2  |
  | 1.8V  | | 3.3V  | | 0.8-  | | 1.2V   |
  |       | |       | | 1.3V  | |        |
  +-------+ +-------+ +-------+ +--------+
       PMIC  (e.g. Rockchip RK808, TI TPS65217)
    

Regülatör türleri

TürVerimlilikGürültüTipik kullanım
LDO (Low Drop-Out)Düşük (%30–80)Çok düşükRF, PLL, ADC, hassas analog
Buck (step-down DCDC)Yüksek (%85–95)Orta (switching)CPU core, GPU, DRAM
Boost (step-up DCDC)YüksekOrtaLED sürücü, USB 5V
Buck-boostYüksekOrtaBatarya deşarjı sırasında sabit ray
Fixed regulator— (yazılım modeli)Sabit gerilim kaynakları
GPIO regulator— (yazılım modeli)GPIO ile kontrol edilen enable

Framework'ün sağladıkları

Constraints denetimimin/max gerilim, max akım sınırları kernel tarafından zorlanır — donanım koruması
Referans sayımıBirden fazla consumer aynı rayı kullanırsa son consumer devre dışı bırakır
DVFS entegrasyonucpufreq, devfreq ile koordineli gerilim-frekans ölçeklendirmesi
Uyku/uyanmasuspend/resume sırasında regülatörleri otomatik yönetme
Sysfs/debugfsÇalışan sistemde gerilim okuma, consumer listesi görüntüleme
NOT

Regulator framework başlık dosyaları: <linux/regulator/consumer.h> (consumer API) ve <linux/regulator/driver.h> (provider API). Consumer sürücüler yalnızca consumer.h'ı include etmeli.

Bu bölümde

  • Regulator framework: PMIC güç raylarını merkezi, güvenli yöneten kernel alt sistemi
  • LDO (düşük gürültü) vs Buck/Boost DCDC (yüksek verimlilik) farkı
  • Constraints denetimi, referans sayımı, DVFS entegrasyonu
  • consumer.h (tüketici) / driver.h (sağlayıcı) — include ayrımı önemli

01 Consumer API — regulator_get, enable, set_voltage

Consumer sürücülerin güç raylarını nasıl alacağı, etkinleştireceği ve gerilimi nasıl ayarlayacağı.

Handle alma

consumer_driver.c — regulator_get
#include <linux/regulator/consumer.h>

/* Temel alma — remove()'da regulator_put() zorunlu */
struct regulator *vdd = regulator_get(&pdev->dev, "vdd");
if (IS_ERR(vdd))
    return PTR_ERR(vdd);

/* devm versiyonu — otomatik cleanup, probe'da tercih edin */
struct regulator *vcore = devm_regulator_get(&pdev->dev, "vcore");
if (IS_ERR(vcore))
    return dev_err_probe(&pdev->dev, PTR_ERR(vcore),
                         "vcore alinamadi\n");

/* İsteğe bağlı regülatör — yoksa NULL döner, hata değil */
struct regulator *vopt = devm_regulator_get_optional(&pdev->dev, "vopt");
if (IS_ERR(vopt)) {
    if (PTR_ERR(vopt) == -ENODEV)
        vopt = NULL;  /* regülatör yok — tamam */
    else
        return PTR_ERR(vopt);
}

Enable / Disable

consumer_driver.c — enable/disable
/* Enable — may sleep (I2C/SPI PMIC protokolü sürebilir) */
int ret = regulator_enable(vcore);
if (ret) {
    dev_err(&pdev->dev, "vcore enable edilemedi: %d\n", ret);
    return ret;
}

/* Enable durumu sorgusu */
int enabled = regulator_is_enabled(vcore);
/* 1: enabled, 0: disabled, negatif: hata */

/* Disable */
regulator_disable(vcore);

/* Force disable — referans sayacını sıfıra zorla (dikkatli kullanın!) */
regulator_force_disable(vcore);

Gerilim ayarlama

consumer_driver.c — voltage API
/* Mevcut gerilimi oku (mikroVolt cinsinden) */
int voltage = regulator_get_voltage(vcore);
if (voltage < 0)
    dev_warn(&pdev->dev, "gerilim okunamadi: %d\n", voltage);
else
    dev_info(&pdev->dev, "vcore: %d uV\n", voltage);

/* Gerilim aralığı ayarla (uV cinsinden min, max) */
ret = regulator_set_voltage(vcore, 900000, 1100000);
/* Framework: constraints aralığında mı? Yoksa -EINVAL */

/* Tam olarak bir gerilim (min == max) */
ret = regulator_set_voltage(vcore, 1000000, 1000000);  /* 1.0 V */

/* Akım sınırı ayarla (mikroAmper) */
ret = regulator_set_current_limit(vdd, 500000, 1000000);

/* Gerilim aralığını sorgula */
int min_uv = regulator_get_linear_step(vcore);
int max_sel = regulator_count_voltages(vcore);

Tam consumer örneği — GPU sürücüsü

gpu_driver.c — probe() regulator yönetimi
struct my_gpu_priv {
    struct regulator *vdd_core;
    struct regulator *vdd_io;
    unsigned int      cur_freq_khz;
};

static const struct {
    unsigned int freq_khz;
    int          min_uv;
    int          max_uv;
} gpu_opp_table[] = {
    { 200000, 800000, 900000  },  /* 200 MHz — 0.8–0.9 V */
    { 400000, 900000, 1000000 },  /* 400 MHz — 0.9–1.0 V */
    { 600000, 950000, 1100000 },  /* 600 MHz — 0.95–1.1 V */
};

static int gpu_set_freq(struct my_gpu_priv *priv,
                         unsigned int target_khz)
{
    int i, ret;

    for (i = 0; i < ARRAY_SIZE(gpu_opp_table); i++) {
        if (gpu_opp_table[i].freq_khz == target_khz)
            break;
    }
    if (i == ARRAY_SIZE(gpu_opp_table))
        return -EINVAL;

    /* Frekans artışı: önce gerilimi yükselt */
    if (target_khz > priv->cur_freq_khz) {
        ret = regulator_set_voltage(priv->vdd_core,
                                    gpu_opp_table[i].min_uv,
                                    gpu_opp_table[i].max_uv);
        if (ret)
            return ret;
        /* sonra clock değiştir... */
    } else {
        /* Frekans düşüşü: önce clock düşür, sonra gerilimi */
        /* ... clock değiştir ... */
        ret = regulator_set_voltage(priv->vdd_core,
                                    gpu_opp_table[i].min_uv,
                                    gpu_opp_table[i].max_uv);
        if (ret)
            return ret;
    }
    priv->cur_freq_khz = target_khz;
    return 0;
}
ÖNEMLİ

DVFS (Dynamic Voltage and Frequency Scaling) sırasında doğru sıra kritiktir: frekans artışında önce gerilimi yükselt, sonra frekansı artır. Frekans düşüşünde önce frekansı düşür, sonra gerilimi azalt. Sıra hatası, SoC'un stabil çalışmamasına neden olur.

Bu bölümde

  • devm_regulator_get() → IS_ERR() → -EPROBE_DEFER akışı
  • regulator_get_optional(): olmayan regülatörler için -ENODEV → NULL dönüşümü
  • set_voltage(min_uv, max_uv): framework constraints ile çakışırsa -EINVAL
  • DVFS sırası: frekans artışı → önce voltage up; frekans düşüşü → önce freq down

02 regulator_desc ve regulator_ops — driver tarafı API

PMIC sürücüsünün kernel'e sunduğu regulator_desc yapısı ve ops callback kümesi.

regulator_desc yapısı

include/linux/regulator/driver.h — regulator_desc (önemli alanlar)
struct regulator_desc {
    const char              *name;          /* "ldo1", "buck2" vb. */
    const char              *supply_name;   /* parent supply ismi */
    const char              *of_match;      /* DT compatible eşleşmesi */

    unsigned int             id;
    unsigned int             type;          /* REGULATOR_VOLTAGE / REGULATOR_CURRENT */

    const struct regulator_ops *ops;

    /* Gerilim tablosu (lineer veya liste) */
    const struct regulator_linear_range *linear_ranges;
    int                      n_linear_ranges;

    const unsigned int      *volt_table;    /* Non-lineer voltajlar */
    unsigned int             n_voltages;    /* Seçici sayısı */

    /* Register adresleri (regmap kullanırken) */
    unsigned int             vsel_reg;      /* Gerilim seçici register */
    unsigned int             vsel_mask;
    unsigned int             enable_reg;
    unsigned int             enable_mask;
    bool                     enable_is_inverted;

    unsigned int             ramp_delay;    /* uV/us cinsinden */
    unsigned int             off_on_delay;  /* us — enable sonrası stabilizasyon */

    struct module           *owner;
};

regulator_ops callback'leri

my_pmic_ldo.c — regulator_ops
/* LDO enable/disable — I2C register yazımı */
static int my_ldo_enable(struct regulator_dev *rdev)
{
    struct my_pmic *pmic = rdev_get_drvdata(rdev);
    unsigned int   reg  = rdev->desc->enable_reg;
    unsigned int   mask = rdev->desc->enable_mask;

    return regmap_update_bits(pmic->regmap, reg, mask, mask);
}

static int my_ldo_disable(struct regulator_dev *rdev)
{
    struct my_pmic *pmic = rdev_get_drvdata(rdev);
    return regmap_update_bits(pmic->regmap,
                               rdev->desc->enable_reg,
                               rdev->desc->enable_mask,
                               0);
}

static int my_ldo_is_enabled(struct regulator_dev *rdev)
{
    struct my_pmic *pmic = rdev_get_drvdata(rdev);
    unsigned int val;
    int ret;

    ret = regmap_read(pmic->regmap, rdev->desc->enable_reg, &val);
    if (ret)
        return ret;
    return !!(val & rdev->desc->enable_mask);
}

/* Gerilim seçici get/set — regmap_field vb. */
static int my_ldo_get_voltage_sel(struct regulator_dev *rdev)
{
    struct my_pmic *pmic = rdev_get_drvdata(rdev);
    unsigned int val;
    int ret;

    ret = regmap_read(pmic->regmap, rdev->desc->vsel_reg, &val);
    if (ret)
        return ret;
    return (val & rdev->desc->vsel_mask) >> __ffs(rdev->desc->vsel_mask);
}

static int my_ldo_set_voltage_sel(struct regulator_dev *rdev,
                                   unsigned selector)
{
    struct my_pmic *pmic = rdev_get_drvdata(rdev);
    unsigned int   val  = selector << __ffs(rdev->desc->vsel_mask);

    return regmap_update_bits(pmic->regmap,
                               rdev->desc->vsel_reg,
                               rdev->desc->vsel_mask,
                               val);
}

static const struct regulator_ops my_ldo_ops = {
    .enable            = my_ldo_enable,
    .disable           = my_ldo_disable,
    .is_enabled        = my_ldo_is_enabled,
    .get_voltage_sel   = my_ldo_get_voltage_sel,
    .set_voltage_sel   = my_ldo_set_voltage_sel,
    .list_voltage      = regulator_list_voltage_linear_range,
    .map_voltage       = regulator_map_voltage_linear_range,
};

regulator_ops helper seçimi

CallbackKernel helper (regmap için)Kullanım durumu
list_voltageregulator_list_voltage_linear_rangeLineer gerilim aralığı
list_voltageregulator_list_voltage_tableLineer olmayan volt_table
map_voltageregulator_map_voltage_linear_rangeuV → selector dönüşümü
set_voltage_selregulator_set_voltage_sel_regmapDoğrudan regmap yazımı
get_voltage_selregulator_get_voltage_sel_regmapDoğrudan regmap okuma
enable/disableregulator_enable_regmap / regulator_disable_regmapTek bit enable
is_enabledregulator_is_enabled_regmapBit okuma
İPUCU

Eğer sürücünüz regmap kullanıyorsa ve standart bir lineer gerilim aralığına sahipse, regulator_*_regmap ve regulator_*_linear_range helper'larını kullanarak ops struct'ını minimuma indirebilirsiniz. Birçok üretim PMIC sürücüsü bu şekilde ops tablosunu 2–3 callback'e düşürür.

Bu bölümde

  • regulator_desc: isim, tip, ops, linear_ranges, vsel/enable register tanımları
  • regulator_ops: enable/disable/is_enabled + get/set_voltage_sel + list/map_voltage
  • regmap helper'ları: regulator_enable_regmap, regulator_get_voltage_sel_regmap
  • rdev_get_drvdata(rdev): driver private data'ya ulaşım

03 regulator_register ve devm_regulator_register

PMIC sürücüsünün regülatörleri framework'e nasıl kaydedeceği, regulator_config yapısı ve çoklu regülatör kaydı.

Temel kayıt akışı

my_pmic.c — regulator_register
#include <linux/regulator/driver.h>
#include <linux/regulator/of_regulator.h>

struct my_pmic {
    struct device      *dev;
    struct regmap      *regmap;
    struct regulator_dev *rdvs[MY_NUM_REGULATORS];
};

static const struct regulator_linear_range ldo1_ranges[] = {
    REGULATOR_LINEAR_RANGE(800000,  0,  63, 12500),
    /* min_uV=800000, min_sel=0, max_sel=63, step=12500 uV */
    /* 800 mV'tan başlayarak 63 adımda 12.5 mV artışlı */
};

static const struct regulator_desc my_ldo1_desc = {
    .name            = "LDO1",
    .of_match        = of_match_ptr("ldo1"),
    .regulators_node = of_match_ptr("regulators"),
    .id              = 0,
    .type            = REGULATOR_VOLTAGE,
    .ops             = &my_ldo_ops,
    .linear_ranges   = ldo1_ranges,
    .n_linear_ranges = ARRAY_SIZE(ldo1_ranges),
    .n_voltages      = 64,
    .vsel_reg        = MY_LDO1_VSEL,
    .vsel_mask       = 0x3F,
    .enable_reg      = MY_LDO1_EN,
    .enable_mask     = BIT(0),
    .ramp_delay      = 12500,   /* 12.5 mV/us */
    .off_on_delay    = 200,     /* 200 us stabilizasyon */
    .owner           = THIS_MODULE,
};

static int my_pmic_probe(struct i2c_client *client)
{
    struct my_pmic        *pmic;
    struct regulator_config config = {};
    struct regulator_dev  *rdev;

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

    pmic->dev    = &client->dev;
    pmic->regmap = devm_regmap_init_i2c(client, &my_pmic_regmap_cfg);
    if (IS_ERR(pmic->regmap))
        return PTR_ERR(pmic->regmap);

    /* regulator_config: kayıt sırasında sağlanan runtime bilgiler */
    config.dev         = &client->dev;
    config.driver_data = pmic;         /* rdev_get_drvdata() ile alınır */
    config.regmap      = pmic->regmap;
    config.of_node     = client->dev.of_node;

    /* devm_ versiyonu — otomatik unregister */
    rdev = devm_regulator_register(&client->dev, &my_ldo1_desc, &config);
    if (IS_ERR(rdev))
        return dev_err_probe(&client->dev, PTR_ERR(rdev),
                             "LDO1 kaydi basarisiz\n");

    pmic->rdvs[0] = rdev;
    i2c_set_clientdata(client, pmic);
    return 0;
}

Çoklu regülatör kaydı döngüsü

my_pmic.c — döngüsel kayıt
static const struct regulator_desc my_pmic_descs[] = {
    [0] = { .name = "LDO1", .of_match = of_match_ptr("ldo1"), /* ... */ },
    [1] = { .name = "LDO2", .of_match = of_match_ptr("ldo2"), /* ... */ },
    [2] = { .name = "BUCK1",.of_match = of_match_ptr("buck1"),/* ... */ },
};

static int my_pmic_probe(struct i2c_client *client)
{
    struct my_pmic *pmic = /* ... init ... */
    struct regulator_config config = {};
    int i;

    config.dev    = &client->dev;
    config.regmap = pmic->regmap;

    for (i = 0; i < ARRAY_SIZE(my_pmic_descs); i++) {
        config.driver_data = pmic;
        config.of_node     = of_get_child_by_name(client->dev.of_node,
                                                   my_pmic_descs[i].of_match);
        pmic->rdvs[i] = devm_regulator_register(&client->dev,
                                                  &my_pmic_descs[i],
                                                  &config);
        if (IS_ERR(pmic->rdvs[i]))
            return dev_err_probe(&client->dev,
                                  PTR_ERR(pmic->rdvs[i]),
                                  "%s kaydi basarisiz\n",
                                  my_pmic_descs[i].name);
    }
    return 0;
}

Bu bölümde

  • REGULATOR_LINEAR_RANGE(min_uV, min_sel, max_sel, step_uV) makrosu
  • regulator_config: dev, driver_data, regmap, of_node — kayıt runtime bilgisi
  • devm_regulator_register(): otomatik unregister, probe'da tercih edilmeli
  • Çoklu desc dizisi + döngüsel kayıt — standart PMIC sürücü paterni

04 Device Tree regulator binding

DT binding standartları: regulator-min/max-microvolt, regulator-always-on, regulator-boot-on ve supplier-consumer bağlantıları.

PMIC node ve regulator alt node'ları

my-board.dts — PMIC DT binding
&i2c0 {
    pmic: pmic@20 {
        compatible = "myvendor,my-pmic";
        reg = <0x20>;
        interrupt-parent = <&gpio2>;
        interrupts = <5 IRQ_TYPE_LEVEL_LOW>;

        regulators {
            ldo1_reg: ldo1 {
                regulator-name          = "VDD_CAM_1V8";
                regulator-min-microvolt = <1700000>;  /* 1.7 V */
                regulator-max-microvolt = <1900000>;  /* 1.9 V */
                regulator-always-on;   /* suspend sırasında da açık */
            };

            ldo2_reg: ldo2 {
                regulator-name          = "VDD_CODEC_3V3";
                regulator-min-microvolt = <3300000>;
                regulator-max-microvolt = <3300000>;  /* sabit */
                regulator-boot-on;     /* bootloader açmışsa kapat */
            };

            buck1_reg: buck1 {
                regulator-name          = "VDD_CPU";
                regulator-min-microvolt = <800000>;   /* 0.8 V */
                regulator-max-microvolt = <1100000>;  /* 1.1 V */
                regulator-always-on;
                regulator-ramp-delay    = <12500>;    /* uV/us */
            };

            buck2_reg: buck2 {
                regulator-name          = "VDD_DRAM_1V1";
                regulator-min-microvolt = <1050000>;
                regulator-max-microvolt = <1150000>;
                regulator-always-on;
                /* DT'de soft-start zamanı */
                regulator-soft-start;
            };
        };
    };
};

Consumer'ların regülatöre bağlanması

my-board.dts — consumer bağlantısı
/* Kamera sürücüsü — ldo1 ve ldo2'yi kullanıyor */
&camera0 {
    status = "okay";

    /*
     * supply bağlaması: "vdd" ismi consumer sürücüdeki
     * devm_regulator_get(&dev, "vdd") ile eşleşir
     */
    vdd-supply      = <&ldo1_reg>;   /* 1.8 V analog */
    vddio-supply    = <&ldo2_reg>;   /* 3.3 V I/O */
};

/* CPU cluster — buck1 vdd-supply */
&cpu0 {
    cpu-supply = <&buck1_reg>;
};

/* GPU */
&gpu {
    mali-supply = <&buck1_reg>;
};

/* Regülatörler arası bağımlılık: ldo2 buck2'ye bağlı */
&ldo2_reg {
    vin-supply = <&buck2_reg>;
};

Standart DT property tablosu

PropertyTürAnlamı
regulator-namestringİnsan-okunabilir isim (loglarda görünür)
regulator-min-microvoltu32İzin verilen minimum gerilim (uV)
regulator-max-microvoltu32İzin verilen maksimum gerilim (uV)
regulator-always-onbooleanHiçbir zaman disable edilmez
regulator-boot-onbooleanAçılışta enable edilmiş; shutdown uygulanır
regulator-ramp-delayu32uV/us — gerilim değişim hızı
regulator-min-microampu32Minimum akım kısıtı
regulator-max-microampu32Maksimum akım kısıtı
regulator-allow-bypassbooleanLDO bypass moduna izin ver
regulator-soft-startbooleanSoft-start özelliğini etkinleştir
DİKKAT

regulator-always-on ile regulator-boot-on farkı: always-on hiçbir koşulda disable edilmez. boot-on ise "açılışta enable gelmiş" anlamına gelir — consumer disable ederse framework disable eder. Güç tasarrufu için yanlış flag seçimi enerji tasarrufunu engeller.

Bu bölümde

  • regulators {}: PMIC node içindeki alt node'lar — her biri bir regülatörü temsil eder
  • *-supply = <&ldo1_reg>: consumer'ı regülatöre bağlar; isim "vdd-supply" → "vdd"
  • regulator-always-on: hiçbir zaman disable; regulator-boot-on: açılışta enable
  • vin-supply: regülatörler arası kaynak bağımlılığı (kaskad)

05 Regulator constraints ve regulator_init_data

Kernel içi kısıt yapısı: regulator_constraints, güvenlik sınırları ve platform init_data.

DT'den veya platform data'dan alınan kısıtlar struct regulator_constraints'a dönüştürülür. Framework, her set_voltage ve enable çağrısını bu kısıtlara karşı doğrular — böylece yanlış gerilim yazımı kernel düzeyinde engellenir.

regulator_constraints yapısı

include/linux/regulator/machine.h — regulator_constraints
struct regulator_constraints {
    const char    *name;

    /* Gerilim kısıtları (uV) */
    int    min_uV;
    int    max_uV;
    int    uV_offset;   /* Kalibrasyon ofseti */

    /* Akım kısıtları (uA) */
    int    min_uA;
    int    max_uA;

    /* Davranış bayrakları */
    unsigned int  always_on:1;
    unsigned int  boot_on:1;
    unsigned int  apply_uV:1;   /* Kayıt sırasında uV uygula */
    unsigned int  ramp_disable:1;
    unsigned int  soft_start:1;
    unsigned int  over_current_protection:1;

    /* İzin verilen modlar */
    unsigned int  valid_modes_mask;
    unsigned int  valid_ops_mask;   /* REGULATOR_CHANGE_VOLTAGE | _STATUS | _MODE */

    int    ramp_delay;       /* uV/us */
    int    settling_time;   /* us */
    int    enable_time;     /* us — enable sonrası bekleme */
};

Platform board init_data (DT olmayan sistemler için)

board_init.c — regulator_init_data
static struct regulator_consumer_supply ldo1_consumers[] = {
    REGULATOR_SUPPLY("vdd", "0-003c"),   /* kamera I2C adresi 0x3c */
    REGULATOR_SUPPLY("avdd", "spi0.0"),  /* SPI slave 0 */
};

static struct regulator_init_data my_ldo1_init_data = {
    .constraints = {
        .name       = "VDD_CAM_1V8",
        .min_uV     = 1700000,
        .max_uV     = 1900000,
        .valid_ops_mask = REGULATOR_CHANGE_VOLTAGE |
                          REGULATOR_CHANGE_STATUS,
        .always_on  = 0,
        .boot_on    = 0,
    },
    .num_consumer_supplies = ARRAY_SIZE(ldo1_consumers),
    .consumer_supplies     = ldo1_consumers,
};

/* PMIC platform data */
static struct my_pmic_platform_data my_pmic_pdata = {
    .regulators[0] = &my_ldo1_init_data,
    .regulators[1] = &my_ldo2_init_data,
    .regulators[2] = &my_buck1_init_data,
};

valid_ops_mask değerleri

FlagAnlamı
REGULATOR_CHANGE_VOLTAGEConsumer set_voltage() çağırabilir
REGULATOR_CHANGE_CURRENTConsumer set_current_limit() çağırabilir
REGULATOR_CHANGE_MODEConsumer çalışma modunu değiştirebilir
REGULATOR_CHANGE_STATUSConsumer enable/disable yapabilir
REGULATOR_CHANGE_DRMSDynamic Regulator Mode Switching izinli
REGULATOR_CHANGE_BYPASSLDO bypass moduna geçiş izinli
GÜVENLİK

valid_ops_mask yanlış yapılandırılırsa consumer set_voltage() çağırdığında -EPERM hatası alır. Üretim sistemlerinde kısıt maskesini minimum tutun: yalnızca gerekli ops'ları açın. Bu, yazılım hatalarının donanıma zarar vermesini önler.

Bu bölümde

  • regulator_constraints: framework'ün her set_voltage/enable'ı doğruladığı kısıt yapısı
  • valid_ops_mask: hangi consumer işlemlerine izin verildiğini belirler
  • apply_uV: kayıt sırasında doğrudan gerilim uygula (tek uV değeri için)
  • Platform init_data: DT olmayan sistemlerde consumer-supply eşleştirmesi

06 Sabit regülatörler — fixed-regulator, gpio-regulator

Ayrı PMIC olmadan donanım regülatörlerini modelleyen generic kernel sürücüleri.

fixed-regulator

Sabit gerilimli ve her zaman açık olan güç kaynaklarını (anakart voltage rail'leri, linear regulatörler) modellemek için fixed-regulator generic binding kullanılır. Kernel sürücüsü drivers/regulator/fixed.c'dedir.

board.dts — fixed-regulator örnekleri
/ {
    /* Her zaman açık 3.3 V rail */
    vcc_3v3: regulator-vcc3v3 {
        compatible      = "regulator-fixed";
        regulator-name  = "VCC_3V3";
        regulator-min-microvolt = <3300000>;
        regulator-max-microvolt = <3300000>;
        regulator-always-on;
    };

    /* GPIO ile kontrol edilen enable */
    vdd_wifi: regulator-vdd-wifi {
        compatible      = "regulator-fixed";
        regulator-name  = "VDD_WIFI_1V8";
        regulator-min-microvolt = <1800000>;
        regulator-max-microvolt = <1800000>;
        gpio            = <&gpio3 12 GPIO_ACTIVE_HIGH>;
        enable-active-high;          /* GPIO high = enable */
        startup-delay-us = <5000>;  /* 5 ms stabilizasyon */
        vin-supply = <&vcc_3v3>;    /* kaynak */
    };

    /* GPIO active-low örneği */
    vdd_cam: regulator-vdd-cam {
        compatible      = "regulator-fixed";
        regulator-name  = "VDD_CAM_2V8";
        regulator-min-microvolt = <2800000>;
        regulator-max-microvolt = <2800000>;
        gpio            = <&gpio1 8 GPIO_ACTIVE_LOW>;
        /* enable-active-high yok = active-low enable */
    };
};

gpio-regulator

gpio-regulator iki ya da daha fazla gerilim düzeyi arasında GPIO ile seçim yapan regülatörleri modellemek için kullanılır. Kernel sürücüsü drivers/regulator/gpio-regulator.c'dedir.

board.dts — gpio-regulator
/* GPIO ile 1.8V / 3.3V seçimi yapan I/O regülatörü */
vccq_sd: regulator-vccq-sd {
    compatible = "regulator-gpio";
    regulator-name = "VCCQ_SD";
    regulator-min-microvolt = <1800000>;
    regulator-max-microvolt = <3300000>;
    gpios = <&gpio0 7 GPIO_ACTIVE_HIGH>;
    gpios-states = <0>;           /* başlangıç durumu */
    states = <1800000 0            /* GPIO=0: 1.8 V */
               3300000 1>;         /* GPIO=1: 3.3 V */
    enable-active-high;
    startup-delay-us = <200>;
};

Yazılımda fixed/gpio regülatör kullanımı

consumer_driver.c — fixed regülatör
/* Fixed/GPIO regülatörler de aynı consumer API ile kullanılır */
struct regulator *vdd_wifi = devm_regulator_get(&pdev->dev, "vdd");
if (IS_ERR(vdd_wifi))
    return dev_err_probe(&pdev->dev, PTR_ERR(vdd_wifi),
                         "WiFi regulator alinamadi\n");

ret = regulator_enable(vdd_wifi);
if (ret)
    return ret;

/* startup-delay-us DT'de tanımlandıysa framework otomatik bekler */
/* Manuel bekleme gerekmez — framework halleder */

/* vccq_sd: voltage mux için set_voltage ile GPIO toggle */
struct regulator *vccq = devm_regulator_get(&mmc->dev, "vccq");
ret = regulator_set_voltage(vccq, 1800000, 1800000);  /* 1.8V seç */
/* Arka planda gpio-regulator sürücüsü GPIO'yu toggle eder */
regulator-fixedSabit gerilim; opsiyonel GPIO enable; startup-delay-us otomatik uygulanır
regulator-gpioGPIO ile gerilim seviyesi seçimi; states tablosu uV ↔ GPIO bit mask eşleşmesi
enable-active-highGPIO HIGH = enable; yoksa GPIO LOW = enable (active-low)
vin-supplyKaynak regülatör — kaskad güç açma/kapama sırası için önemli

Bu bölümde

  • regulator-fixed: sabit gerilim, opsiyonel GPIO enable — ayrı sürücü gerekmez
  • regulator-gpio: GPIO ile birden fazla gerilim seçimi, states tablosu
  • startup-delay-us: framework enable sonrası otomatik bekler
  • Consumer API aynı — sürücü fixed/gpio/PMIC farkını bilmez

07 regulator notifier ve event sistemi

Consumer sürücüler regülatör olaylarını (over-current, under-voltage, failure) dinlemek için notifier kaydedebilir.

Regulator framework, donanım korumaları veya yazılım kısıt ihlalleri oluştuğunda event yayar. Consumer sürücüler bu olayları dinleyerek reaktif güç yönetimi uygulayabilir — örneğin aşırı akımda işlem yükünü azaltma.

regulator_register_notifier kullanımı

consumer_notifier.c
#include <linux/regulator/consumer.h>

struct my_dev_priv {
    struct regulator      *vdd;
    struct notifier_block  nb;
};

static int my_regulator_event(struct notifier_block *nb,
                               unsigned long         event,
                               void                 *data)
{
    struct my_dev_priv *priv =
        container_of(nb, struct my_dev_priv, nb);

    switch (event) {
    case REGULATOR_EVENT_UNDER_VOLTAGE:
        /* Düşük gerilim — performansı azalt */
        dev_warn(NULL, "VDD dusuk gerilim! Performans azaltiliyor.\n");
        my_dev_throttle(priv);
        break;

    case REGULATOR_EVENT_OVER_CURRENT:
        /* Aşırı akım — donanımı koru */
        dev_err(NULL, "VDD asiri akim! Cihaz kapatiliyor.\n");
        my_dev_emergency_shutdown(priv);
        break;

    case REGULATOR_EVENT_VOLTAGE_CHANGE:
        /* Gerilim değişti — yeniden yapılandır */
        {
            int new_uv = regulator_get_voltage(priv->vdd);
            dev_info(NULL, "Yeni VDD: %d uV\n", new_uv);
            my_dev_reconfigure(priv, new_uv);
        }
        break;

    case REGULATOR_EVENT_DISABLE:
        dev_warn(NULL, "VDD devre disi birakildi!\n");
        break;

    case REGULATOR_EVENT_FAILURE:
        dev_crit(NULL, "VDD arizasi! Acil kapatma.\n");
        kernel_power_off();
        break;
    }
    return NOTIFY_OK;
}

static int my_dev_probe(struct platform_device *pdev)
{
    struct my_dev_priv *priv;
    int ret;

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

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

    priv->nb.notifier_call = my_regulator_event;

    ret = devm_regulator_register_notifier(priv->vdd, &priv->nb);
    if (ret) {
        dev_err(&pdev->dev, "notifier kaydi basarisiz: %d\n", ret);
        return ret;
    }

    return 0;
}

/* Manuel unregister — devm kullanılmadıysa */
static int my_dev_remove(struct platform_device *pdev)
{
    struct my_dev_priv *priv = platform_get_drvdata(pdev);
    regulator_unregister_notifier(priv->vdd, &priv->nb);
    return 0;
}

Regulator event listesi

EventKaynakTipik aksiyon
REGULATOR_EVENT_UNDER_VOLTAGEDonanım OVP/UVPPerformans kısıtla
REGULATOR_EVENT_OVER_CURRENTDonanım OCPYük azalt veya kapat
REGULATOR_EVENT_OVER_TEMPTermal korumaThermal throttle
REGULATOR_EVENT_VOLTAGE_CHANGEset_voltage sonrasıBağımlı donanımı yeniden ayarla
REGULATOR_EVENT_DISABLEregulator_disable()Log, emergency stop
REGULATOR_EVENT_ENABLEregulator_enable()Başlangıç gecikme kontrolü
REGULATOR_EVENT_FAILUREDonanım arızasıKernel panic / güvenli kapatma
REGULATOR_EVENT_PRE_DISABLEdisable öncesiGraceful shutdown

Bu bölümde

  • devm_regulator_register_notifier(): otomatik cleanup ile notifier kaydı
  • REGULATOR_EVENT_*: donanım OCP/UVP olayları + yazılım enable/disable olayları
  • container_of ile notifier_block'tan driver priv data'ya erişim
  • REGULATOR_EVENT_FAILURE: kernel_power_off() için güvenli kapatma noktası

08 Hata ayıklama — debugfs regulator/, regulator_summary

/sys/kernel/debug/regulator/ altındaki debugfs arayüzü ve regulator_summary ile sistemin güç durumunu analiz etme.

debugfs regulator dosyaları

bash — debugfs regulator keşfi
# debugfs mount
mount -t debugfs none /sys/kernel/debug

# Tüm regülatörleri listele
ls /sys/kernel/debug/regulator/
# Çıktı: LDO1  LDO2  BUCK1  BUCK2  VCC_3V3 ...

# Belirli regülatörün gerilimini oku
cat /sys/kernel/debug/regulator/LDO1/voltage
# Çıktı: 1800000

# Enable durumu
cat /sys/kernel/debug/regulator/LDO1/state
# Çıktı: enabled

# Kullanım sayısı
cat /sys/kernel/debug/regulator/LDO1/use_count
# Çıktı: 2  (2 consumer enable etti)

# Consumer listesi
cat /sys/kernel/debug/regulator/LDO1/consumers
# Çıktı:
# Device-Supply: camera0 vdd
# Device-Supply: spi0.0  avdd

regulator_summary — tam sistem görünümü

bash — regulator_summary
# Tüm regülatör hiyerarşisi
cat /sys/kernel/debug/regulator/regulator_summary

# Örnek çıktı:
# regulator                      use open bypass  voltage current     min     max
# -------------------------------------------------------------------------------
# VCC_3V3                          0    1      0  3300000       0 3300000 3300000
#   LDO1                           2    2      0  1800000       0 1700000 1900000
#     camera0-vdd                  1    0      0
#     spi0.0-avdd                  1    0      0
#   LDO2                           0    1      0  3300000       0 3300000 3300000
#   BUCK1                          1    3      0  1000000       0  800000 1100000
#     cpu0-cpu-supply              1    0      0

sysfs regulator arayüzü

bash — sysfs regulator
# /sys/class/regulator/ altındaki her regülatör
ls /sys/class/regulator/
# regulator.0  regulator.1  regulator.2 ...

# İsim
cat /sys/class/regulator/regulator.0/name
# LDO1

# Desteklenen gerilim adımları
cat /sys/class/regulator/regulator.0/num_voltages
# 64

# Minimum gerilim
cat /sys/class/regulator/regulator.0/min_microvolts
# 1700000

Yaygın sorunlar ve çözümleri

BelirtiOlası nedenKontrol
-EPROBE_DEFERPMIC sürücüsü henüz yüklenmemişdmesg | grep "PMIC"; modprobe sırası
set_voltage: -EINVALİstenen gerilim constraints dışındaregulator_summary min/max aralığını kontrol et
set_voltage: -EPERMvalid_ops_mask'ta CHANGE_VOLTAGE yokDT / init_data constraints incelemesi
enable: -EACCESCHANGE_STATUS izni yokvalid_ops_mask kontrolü
use_count sıfırlanmıyorregulator_enable/disable dengesizliğiHer enable için disable çağrıldığından emin ol
Regülatör kapanmıyoralways-on veya başka consumer tutmuşconsumers dosyasına bak

Kernel config gereksinimleri

Kconfig
CONFIG_REGULATOR=y
CONFIG_REGULATOR_DEBUG=y          # debugfs arayüzü
CONFIG_REGULATOR_FIXED_VOLTAGE=y  # fixed-regulator sürücüsü
CONFIG_REGULATOR_GPIO=y           # gpio-regulator sürücüsü
CONFIG_DEBUG_FS=y                 # debugfs genel

Bu bölümde

  • /sys/kernel/debug/regulator/<name>/: voltage, state, use_count, consumers
  • regulator_summary: tüm hiyerarşiyi use/open/bypass/voltage/current/min/max ile listeler
  • use_count dengesizliği: en yaygın hata — her enable için eşleşen disable zorunlu
  • -EPROBE_DEFER: PMIC modülü yüklenmemiş; -EPERM: valid_ops_mask eksikliği