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ür | Verimlilik | Gürültü | Tipik kullanım |
|---|---|---|---|
| LDO (Low Drop-Out) | Düşük (%30–80) | Çok düşük | RF, PLL, ADC, hassas analog |
| Buck (step-down DCDC) | Yüksek (%85–95) | Orta (switching) | CPU core, GPU, DRAM |
| Boost (step-up DCDC) | Yüksek | Orta | LED sürücü, USB 5V |
| Buck-boost | Yüksek | Orta | Batarya 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ı
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
#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
/* 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
/* 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ü
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;
}
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ı
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
/* 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
| Callback | Kernel helper (regmap için) | Kullanım durumu |
|---|---|---|
| list_voltage | regulator_list_voltage_linear_range | Lineer gerilim aralığı |
| list_voltage | regulator_list_voltage_table | Lineer olmayan volt_table |
| map_voltage | regulator_map_voltage_linear_range | uV → selector dönüşümü |
| set_voltage_sel | regulator_set_voltage_sel_regmap | Doğrudan regmap yazımı |
| get_voltage_sel | regulator_get_voltage_sel_regmap | Doğrudan regmap okuma |
| enable/disable | regulator_enable_regmap / regulator_disable_regmap | Tek bit enable |
| is_enabled | regulator_is_enabled_regmap | Bit okuma |
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ışı
#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ü
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ı
&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ı
/* 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
| Property | Tür | Anlamı |
|---|---|---|
| regulator-name | string | İnsan-okunabilir isim (loglarda görünür) |
| regulator-min-microvolt | u32 | İzin verilen minimum gerilim (uV) |
| regulator-max-microvolt | u32 | İzin verilen maksimum gerilim (uV) |
| regulator-always-on | boolean | Hiçbir zaman disable edilmez |
| regulator-boot-on | boolean | Açılışta enable edilmiş; shutdown uygulanır |
| regulator-ramp-delay | u32 | uV/us — gerilim değişim hızı |
| regulator-min-microamp | u32 | Minimum akım kısıtı |
| regulator-max-microamp | u32 | Maksimum akım kısıtı |
| regulator-allow-bypass | boolean | LDO bypass moduna izin ver |
| regulator-soft-start | boolean | Soft-start özelliğini etkinleştir |
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ı
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)
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
| Flag | Anlamı |
|---|---|
| REGULATOR_CHANGE_VOLTAGE | Consumer set_voltage() çağırabilir |
| REGULATOR_CHANGE_CURRENT | Consumer set_current_limit() çağırabilir |
| REGULATOR_CHANGE_MODE | Consumer çalışma modunu değiştirebilir |
| REGULATOR_CHANGE_STATUS | Consumer enable/disable yapabilir |
| REGULATOR_CHANGE_DRMS | Dynamic Regulator Mode Switching izinli |
| REGULATOR_CHANGE_BYPASS | LDO bypass moduna geçiş izinli |
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.
/ {
/* 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.
/* 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ı
/* 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 */
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ı
#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
| Event | Kaynak | Tipik aksiyon |
|---|---|---|
| REGULATOR_EVENT_UNDER_VOLTAGE | Donanım OVP/UVP | Performans kısıtla |
| REGULATOR_EVENT_OVER_CURRENT | Donanım OCP | Yük azalt veya kapat |
| REGULATOR_EVENT_OVER_TEMP | Termal koruma | Thermal throttle |
| REGULATOR_EVENT_VOLTAGE_CHANGE | set_voltage sonrası | Bağımlı donanımı yeniden ayarla |
| REGULATOR_EVENT_DISABLE | regulator_disable() | Log, emergency stop |
| REGULATOR_EVENT_ENABLE | regulator_enable() | Başlangıç gecikme kontrolü |
| REGULATOR_EVENT_FAILURE | Donanım arızası | Kernel panic / güvenli kapatma |
| REGULATOR_EVENT_PRE_DISABLE | disable öncesi | Graceful 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ı
# 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ü
# 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ü
# /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
| Belirti | Olası neden | Kontrol |
|---|---|---|
| -EPROBE_DEFER | PMIC sürücüsü henüz yüklenmemiş | dmesg | grep "PMIC"; modprobe sırası |
| set_voltage: -EINVAL | İstenen gerilim constraints dışında | regulator_summary min/max aralığını kontrol et |
| set_voltage: -EPERM | valid_ops_mask'ta CHANGE_VOLTAGE yok | DT / init_data constraints incelemesi |
| enable: -EACCES | CHANGE_STATUS izni yok | valid_ops_mask kontrolü |
| use_count sıfırlanmıyor | regulator_enable/disable dengesizliği | Her enable için disable çağrıldığından emin ol |
| Regülatör kapanmıyor | always-on veya başka consumer tutmuş | consumers dosyasına bak |
Kernel config gereksinimleri
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