|
|
1 неделя назад | |
|---|---|---|
| .. | ||
| README.md | 1 неделя назад | |
| README_zh.md | 1 неделя назад | |
The Regulator framework in RT-Thread provides a standardized interface for managing voltage and current regulators in embedded systems. Regulators are essential components that provide stable power supplies to various hardware peripherals, processors, and other system components.
Voltage regulators are electronic circuits that maintain a constant voltage level regardless of changes in load current or input voltage. They are crucial for:
Common regulator types include:
The RT-Thread regulator framework, located in components/drivers/regulator/, provides:
Architecture:
┌─────────────────────────────────────────────────────────┐
│ Consumer Drivers │
│ (UART, SPI, I2C, MMC, CPU, etc.) │
└────────────────────┬────────────────────────────────────┘
│ Consumer API
│ (get, enable, set_voltage, etc.)
┌────────────────────┴────────────────────────────────────┐
│ Regulator Framework Core │
│ - Reference Counting │
│ - Regulator Tree Management │
│ - Notifier Chains │
│ - Voltage/Current Validation │
└────────────────────┬────────────────────────────────────┘
│ Provider API
│ (ops callbacks)
┌────────────────────┴────────────────────────────────────┐
│ Regulator Drivers │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ Fixed │ │ GPIO │ │ SCMI │ │
│ │ Regulator │ │ Regulator │ │ Regulator │ │
│ └─────────────┘ └─────────────┘ └─────────────┘ │
└────────────────────┬────────────────────────────────────┘
│
┌────────────────────┴────────────────────────────────────┐
│ Hardware Regulators │
│ (PMIC, discrete regulators, power supplies) │
└──────────────────────────────────────────────────────────┘
The regulator framework depends on the Device Driver Model (DM):
menuconfig RT_USING_REGULATOR
bool "Using Voltage and Current Regulator"
select RT_USING_ADT
select RT_USING_ADT_REF
depends on RT_USING_DM
default n
Location in menuconfig:
RT-Thread Components → Device Drivers → Using Voltage and Current Regulator
Dependencies:
RT_USING_DM: Must be enabled firstRT_USING_ADT: Abstract Data Types (automatic)RT_USING_ADT_REF: Reference counting support (automatic)config RT_REGULATOR_FIXED
bool "Fixed regulator support"
depends on RT_USING_REGULATOR
depends on RT_USING_PIN
depends on RT_USING_PINCTRL
default y
Supports regulators with fixed output voltage, optionally controlled by a GPIO enable pin.
Dependencies:
RT_USING_PIN: GPIO supportRT_USING_PINCTRL: Pin control supportconfig RT_REGULATOR_GPIO
bool "GPIO regulator support"
depends on RT_USING_REGULATOR
depends on RT_USING_PIN
default y
Supports regulators with multiple voltage levels selected by GPIO pins.
Dependencies:
RT_USING_PIN: GPIO supportconfig RT_REGULATOR_SCMI
bool "SCMI regulator support"
depends on RT_USING_REGULATOR
depends on RT_USING_OFW
depends on RT_FIRMWARE_ARM_SCMI
default n
Supports regulators controlled through ARM System Control and Management Interface (SCMI).
Dependencies:
RT_USING_OFW: Device tree supportRT_FIRMWARE_ARM_SCMI: ARM SCMI firmware interfaceif RT_USING_REGULATOR
osource "$(SOC_DM_REGULATOR_DIR)/Kconfig"
endif
This allows SoC-specific regulator drivers to add their own Kconfig options via the SOC_DM_REGULATOR_DIR variable.
All regulator nodes support these standard properties:
regulator-name = "supply_name"; /* Human-readable name */
regulator-min-microvolt = <value>; /* Minimum voltage in µV */
regulator-max-microvolt = <value>; /* Maximum voltage in µV */
regulator-min-microamp = <value>; /* Minimum current in µA */
regulator-max-microamp = <value>; /* Maximum current in µA */
regulator-ramp-delay = <value>; /* Voltage change rate in µV/µs */
regulator-enable-ramp-delay = <value>; /* Enable delay in µs */
regulator-settling-time-us = <value>; /* Settling time in µs */
regulator-settling-time-up-us = <value>; /* Settling time for voltage increase */
regulator-settling-time-down-us = <value>; /* Settling time for voltage decrease */
enable-active-high; /* Enable pin active high (default) */
regulator-boot-on; /* Enable at boot */
regulator-always-on; /* Never disable */
regulator-soft-start; /* Enable soft-start */
regulator-pull-down; /* Enable pull-down when off */
regulator-over-current-protection; /* Enable OCP */
Fixed regulators have a constant output voltage:
regulators {
/* Simple fixed 3.3V regulator */
vcc_3v3: regulator-vcc-3v3 {
compatible = "regulator-fixed";
regulator-name = "vcc-3v3";
regulator-min-microvolt = <3300000>;
regulator-max-microvolt = <3300000>;
regulator-always-on;
regulator-boot-on;
};
/* Fixed regulator with GPIO enable control */
vcc_sd: regulator-vcc-sd {
compatible = "regulator-fixed";
regulator-name = "vcc-sd";
regulator-min-microvolt = <3300000>;
regulator-max-microvolt = <3300000>;
gpio = <&gpio0 10 GPIO_ACTIVE_HIGH>;
pinctrl-names = "default";
pinctrl-0 = <&sd_power_pins>;
enable-active-high;
startup-delay-us = <100000>; /* 100ms */
off-on-delay-us = <10000>; /* 10ms */
};
/* Regulator supplied by another regulator */
vcc_1v8: regulator-vcc-1v8 {
compatible = "regulator-fixed";
regulator-name = "vcc-1v8";
regulator-min-microvolt = <1800000>;
regulator-max-microvolt = <1800000>;
vin-supply = <&vcc_3v3>; /* Parent supply */
regulator-always-on;
};
};
GPIO regulators support multiple voltage levels:
vcc_ddr: regulator-vcc-ddr {
compatible = "regulator-gpio";
regulator-name = "vcc-ddr";
regulator-min-microvolt = <1200000>;
regulator-max-microvolt = <1350000>;
/* GPIO pins to select voltage */
gpios = <&gpio1 5 GPIO_ACTIVE_HIGH>,
<&gpio1 6 GPIO_ACTIVE_HIGH>;
/* Voltage selection table (based on GPIO states) */
states = <1200000 0x0 /* 1.2V: both low */
1250000 0x1 /* 1.25V: pin6=high, pin5=low */
1300000 0x2 /* 1.3V: pin6=low, pin5=high */
1350000 0x3>; /* 1.35V: both high */
enable-gpio = <&gpio1 4 GPIO_ACTIVE_HIGH>;
startup-delay-us = <50000>;
};
Devices reference regulators using supply properties:
/* UART with multiple power supplies */
uart0: serial@10000000 {
compatible = "vendor,uart";
reg = <0x10000000 0x1000>;
interrupts = <32>;
clocks = <&clk_uart0>;
/* Power supplies */
vdd-supply = <&vcc_3v3>; /* Core supply */
vddio-supply = <&vcc_1v8>; /* I/O supply */
status = "okay";
};
/* MMC/SD controller with regulator control */
mmc0: mmc@20000000 {
compatible = "vendor,mmc";
reg = <0x20000000 0x1000>;
vmmc-supply = <&vcc_sd>; /* Card power supply */
vqmmc-supply = <&vcc_1v8>; /* I/O level shifter supply */
status = "okay";
};
The consumer API provides functions for device drivers to manage their power supplies. All operations use opaque struct rt_regulator pointers obtained through the get API.
struct rt_regulator *rt_regulator_get(struct rt_device *dev, const char *id);
Get a regulator for a device.
Parameters:
dev: Pointer to the device structureid: Supply name (matches <name>-supply in device tree, e.g., "vdd", "vmmc")Returns:
Example:
struct rt_device *dev = &pdev->parent;
struct rt_regulator *vdd_reg;
/* Get the "vdd" supply */
vdd_reg = rt_regulator_get(dev, "vdd");
if (!vdd_reg) {
LOG_E("Failed to get vdd regulator");
return -RT_ERROR;
}
void rt_regulator_put(struct rt_regulator *reg);
Release a regulator reference.
Parameters:
reg: Regulator pointer obtained from rt_regulator_get()Example:
rt_regulator_put(vdd_reg);
rt_err_t rt_regulator_enable(struct rt_regulator *reg);
Enable a regulator. Uses reference counting, so multiple enables require matching disables.
Parameters:
reg: Regulator pointerReturns:
RT_EOK on successNotes:
Example:
rt_err_t ret;
ret = rt_regulator_enable(vdd_reg);
if (ret != RT_EOK) {
LOG_E("Failed to enable regulator: %d", ret);
return ret;
}
rt_err_t rt_regulator_disable(struct rt_regulator *reg);
Disable a regulator. Only actually disables when reference count reaches zero.
Parameters:
reg: Regulator pointerReturns:
RT_EOK on successNotes:
regulator-always-on propertyExample:
rt_regulator_disable(vdd_reg);
rt_bool_t rt_regulator_is_enabled(struct rt_regulator *reg);
Check if a regulator is currently enabled.
Parameters:
reg: Regulator pointerReturns:
RT_TRUE if enabledRT_FALSE if disabledExample:
if (rt_regulator_is_enabled(vdd_reg)) {
LOG_I("Regulator is enabled");
}
rt_err_t rt_regulator_set_voltage(struct rt_regulator *reg, int min_uvolt, int max_uvolt);
Set regulator output voltage within a range.
Parameters:
reg: Regulator pointermin_uvolt: Minimum acceptable voltage in microvolts (µV)max_uvolt: Maximum acceptable voltage in microvolts (µV)Returns:
RT_EOK on success-RT_ENOSYS if voltage control not supportedNotes:
Example:
/* Set voltage to 1.8V ±5% */
ret = rt_regulator_set_voltage(vdd_reg, 1710000, 1890000);
if (ret != RT_EOK) {
LOG_E("Failed to set voltage: %d", ret);
}
/* Set precise voltage */
ret = rt_regulator_set_voltage(vdd_reg, 3300000, 3300000);
rt_inline rt_err_t rt_regulator_set_voltage_triplet(struct rt_regulator *reg,
int min_uvolt, int target_uvolt, int max_uvolt);
Set voltage with a preferred target value.
Parameters:
reg: Regulator pointermin_uvolt: Minimum voltage in µVtarget_uvolt: Preferred voltage in µVmax_uvolt: Maximum voltage in µVReturns:
RT_EOK on successNotes:
Example:
/* Prefer 1.8V, but accept 1.71V-1.89V */
ret = rt_regulator_set_voltage_triplet(vdd_reg, 1710000, 1800000, 1890000);
int rt_regulator_get_voltage(struct rt_regulator *reg);
Get current regulator output voltage.
Parameters:
reg: Regulator pointerReturns:
Example:
int voltage = rt_regulator_get_voltage(vdd_reg);
if (voltage > 0) {
LOG_I("Current voltage: %d.%03dV", voltage / 1000000, (voltage / 1000) % 1000);
}
rt_bool_t rt_regulator_is_supported_voltage(struct rt_regulator *reg,
int min_uvolt, int max_uvolt);
Check if a voltage range is supported.
Parameters:
reg: Regulator pointermin_uvolt: Minimum voltage in µVmax_uvolt: Maximum voltage in µVReturns:
RT_TRUE if supportedRT_FALSE if not supportedExample:
if (rt_regulator_is_supported_voltage(vdd_reg, 1800000, 1800000)) {
rt_regulator_set_voltage(vdd_reg, 1800000, 1800000);
}
rt_err_t rt_regulator_set_mode(struct rt_regulator *reg, rt_uint32_t mode);
Set regulator operating mode.
Parameters:
reg: Regulator pointermode: Operating mode flags:
RT_REGULATOR_MODE_FAST: High-speed mode (higher power consumption)RT_REGULATOR_MODE_NORMAL: Normal operation modeRT_REGULATOR_MODE_IDLE: Idle mode (reduced performance)RT_REGULATOR_MODE_STANDBY: Standby mode (minimal power)Returns:
RT_EOK on success-RT_ENOSYS if mode control not supportedExample:
/* Set to low-power mode during idle */
rt_regulator_set_mode(vdd_reg, RT_REGULATOR_MODE_IDLE);
/* Restore normal mode */
rt_regulator_set_mode(vdd_reg, RT_REGULATOR_MODE_NORMAL);
rt_int32_t rt_regulator_get_mode(struct rt_regulator *reg);
Get current regulator operating mode.
Parameters:
reg: Regulator pointerReturns:
RT_REGULATOR_MODE_INVALID on errorrt_err_t rt_regulator_notifier_register(struct rt_regulator *reg,
struct rt_regulator_notifier *notifier);
Register a notifier for regulator events.
Parameters:
reg: Regulator pointernotifier: Notifier structure with callbackReturns:
RT_EOK on successNotifier Structure:
struct rt_regulator_notifier {
rt_list_t list;
struct rt_regulator *regulator;
rt_regulator_notifier_callback callback;
void *priv;
};
typedef rt_err_t (*rt_regulator_notifier_callback)(
struct rt_regulator_notifier *notifier,
rt_ubase_t msg,
void *data);
Event Messages:
RT_REGULATOR_MSG_ENABLE: Regulator enabledRT_REGULATOR_MSG_DISABLE: Regulator disabledRT_REGULATOR_MSG_VOLTAGE_CHANGE: Voltage changed successfullyRT_REGULATOR_MSG_VOLTAGE_CHANGE_ERR: Voltage change failedExample:
static rt_err_t voltage_change_callback(struct rt_regulator_notifier *notifier,
rt_ubase_t msg, void *data)
{
if (msg == RT_REGULATOR_MSG_VOLTAGE_CHANGE) {
union rt_regulator_notifier_args *args = data;
LOG_I("Voltage changed: %d -> %d µV",
args->old_uvolt, args->min_uvolt);
}
return RT_EOK;
}
struct rt_regulator_notifier my_notifier = {
.callback = voltage_change_callback,
.priv = NULL,
};
rt_regulator_notifier_register(vdd_reg, &my_notifier);
rt_err_t rt_regulator_notifier_unregister(struct rt_regulator *reg,
struct rt_regulator_notifier *notifier);
Unregister a regulator notifier.
Parameters:
reg: Regulator pointernotifier: Notifier to unregisterReturns:
RT_EOK on success#include <rtthread.h>
#include <drivers/platform.h>
#include <drivers/regulator.h>
#include <drivers/clk.h>
struct mmc_host {
void *base;
int irq;
struct rt_clk *clk;
struct rt_regulator *vmmc; /* Card power supply */
struct rt_regulator *vqmmc; /* I/O voltage supply */
};
static rt_err_t mmc_set_ios_voltage(struct mmc_host *host, int voltage)
{
rt_err_t ret;
/* Set I/O voltage level */
if (voltage == 1800000) {
ret = rt_regulator_set_voltage(host->vqmmc, 1800000, 1800000);
} else {
ret = rt_regulator_set_voltage(host->vqmmc, 3300000, 3300000);
}
if (ret != RT_EOK) {
LOG_E("Failed to set I/O voltage: %d", ret);
return ret;
}
/* Wait for voltage to stabilize */
rt_thread_mdelay(10);
return RT_EOK;
}
static rt_err_t mmc_power_on(struct mmc_host *host)
{
rt_err_t ret;
/* Enable card power supply */
ret = rt_regulator_enable(host->vmmc);
if (ret != RT_EOK) {
LOG_E("Failed to enable vmmc: %d", ret);
return ret;
}
/* Set I/O voltage to 3.3V initially */
ret = rt_regulator_enable(host->vqmmc);
if (ret != RT_EOK) {
LOG_E("Failed to enable vqmmc: %d", ret);
goto err_disable_vmmc;
}
ret = mmc_set_ios_voltage(host, 3300000);
if (ret != RT_EOK) {
goto err_disable_vqmmc;
}
/* Enable clock */
ret = rt_clk_prepare_enable(host->clk);
if (ret != RT_EOK) {
goto err_disable_vqmmc;
}
return RT_EOK;
err_disable_vqmmc:
rt_regulator_disable(host->vqmmc);
err_disable_vmmc:
rt_regulator_disable(host->vmmc);
return ret;
}
static void mmc_power_off(struct mmc_host *host)
{
/* Disable clock */
rt_clk_disable_unprepare(host->clk);
/* Disable regulators in reverse order */
rt_regulator_disable(host->vqmmc);
rt_regulator_disable(host->vmmc);
}
static rt_err_t mmc_probe(struct rt_platform_device *pdev)
{
rt_err_t ret;
struct rt_device *dev = &pdev->parent;
struct mmc_host *host;
/* Allocate host structure */
host = rt_calloc(1, sizeof(*host));
if (!host)
return -RT_ENOMEM;
/* Map MMIO region */
host->base = rt_dm_dev_iomap(dev, 0);
if (!host->base) {
ret = -RT_ERROR;
goto err_free_host;
}
/* Get IRQ */
host->irq = rt_dm_dev_get_irq(dev, 0);
if (host->irq < 0) {
ret = host->irq;
goto err_iounmap;
}
/* Get clock */
host->clk = rt_clk_get_by_name(dev, "mmc");
if (!host->clk) {
ret = -RT_ERROR;
goto err_iounmap;
}
/* Get regulators */
host->vmmc = rt_regulator_get(dev, "vmmc");
if (!host->vmmc) {
LOG_W("No vmmc regulator");
/* Not fatal, some boards don't have switchable power */
}
host->vqmmc = rt_regulator_get(dev, "vqmmc");
if (!host->vqmmc) {
LOG_W("No vqmmc regulator");
/* Not fatal, fixed I/O voltage is acceptable */
}
/* Power on MMC */
if (host->vmmc || host->vqmmc) {
ret = mmc_power_on(host);
if (ret != RT_EOK) {
goto err_put_regulators;
}
}
/* Initialize MMC hardware */
/* ... */
pdev->priv = host;
LOG_I("MMC host initialized");
return RT_EOK;
err_put_regulators:
if (host->vqmmc)
rt_regulator_put(host->vqmmc);
if (host->vmmc)
rt_regulator_put(host->vmmc);
rt_clk_put(host->clk);
err_iounmap:
rt_iounmap(host->base);
err_free_host:
rt_free(host);
return ret;
}
static rt_err_t mmc_remove(struct rt_platform_device *pdev)
{
struct mmc_host *host = pdev->priv;
/* Power off */
if (host->vmmc || host->vqmmc) {
mmc_power_off(host);
}
/* Release resources */
if (host->vqmmc)
rt_regulator_put(host->vqmmc);
if (host->vmmc)
rt_regulator_put(host->vmmc);
rt_clk_put(host->clk);
rt_iounmap(host->base);
rt_free(host);
return RT_EOK;
}
static const struct rt_ofw_node_id mmc_ofw_ids[] = {
{ .compatible = "vendor,mmc" },
{ /* sentinel */ }
};
static struct rt_platform_driver mmc_driver = {
.name = "mmc",
.ids = mmc_ofw_ids,
.probe = mmc_probe,
.remove = mmc_remove,
};
RT_PLATFORM_DRIVER_EXPORT(mmc_driver);
Implementing a regulator driver involves:
struct rt_regulator_node {
rt_list_t list;
rt_list_t children_nodes;
struct rt_device *dev; /* Parent device */
struct rt_regulator_node *parent; /* Parent regulator */
const char *supply_name; /* Supply name */
const struct rt_regulator_ops *ops; /* Operations */
struct rt_ref ref; /* Reference count */
rt_atomic_t enabled_count; /* Enable count */
const struct rt_regulator_param *param; /* Parameters */
rt_list_t notifier_nodes; /* Notifier list */
void *priv; /* Private data */
};
struct rt_regulator_param {
const char *name; /* Regulator name */
int min_uvolt; /* Minimum voltage in µV */
int max_uvolt; /* Maximum voltage in µV */
int min_uamp; /* Minimum current in µA */
int max_uamp; /* Maximum current in µA */
int ramp_delay; /* Voltage ramp rate in µV/µs */
int enable_delay; /* Enable delay in µs */
int off_on_delay; /* Off-to-on delay in µs */
int settling_time; /* General settling time */
int settling_time_up; /* Voltage increase settling time */
int settling_time_down; /* Voltage decrease settling time */
rt_uint32_t enable_active_high:1; /* Enable pin polarity */
rt_uint32_t boot_on:1; /* Enabled at boot */
rt_uint32_t always_on:1; /* Never disable */
rt_uint32_t soft_start:1; /* Soft-start enabled */
rt_uint32_t pull_down:1; /* Pull-down when off */
rt_uint32_t over_current_protection:1; /* OCP enabled */
rt_uint32_t ramp_disable:1; /* Ramp disabled */
};
struct rt_regulator_ops {
rt_err_t (*enable)(struct rt_regulator_node *reg);
rt_err_t (*disable)(struct rt_regulator_node *reg);
rt_bool_t (*is_enabled)(struct rt_regulator_node *reg);
rt_err_t (*set_voltage)(struct rt_regulator_node *reg, int min_uvolt, int max_uvolt);
int (*get_voltage)(struct rt_regulator_node *reg);
rt_err_t (*set_mode)(struct rt_regulator_node *reg, rt_uint32_t mode);
rt_int32_t (*get_mode)(struct rt_regulator_node *reg);
rt_err_t (*set_ramp_delay)(struct rt_regulator_node *reg, int ramp);
rt_uint32_t (*enable_time)(struct rt_regulator_node *reg);
};
All callbacks are optional. Implement only what the hardware supports.
#include <rtthread.h>
#include <drivers/platform.h>
#include <drivers/regulator.h>
#include <drivers/pin.h>
struct my_regulator {
struct rt_regulator_node reg_node;
struct rt_regulator_param param;
rt_base_t enable_pin;
};
static rt_err_t my_regulator_enable(struct rt_regulator_node *reg_node)
{
struct my_regulator *reg = rt_container_of(reg_node, struct my_regulator, reg_node);
if (reg->enable_pin >= 0) {
rt_pin_mode(reg->enable_pin, PIN_MODE_OUTPUT);
rt_pin_write(reg->enable_pin,
reg->param.enable_active_high ? PIN_HIGH : PIN_LOW);
}
return RT_EOK;
}
static rt_err_t my_regulator_disable(struct rt_regulator_node *reg_node)
{
struct my_regulator *reg = rt_container_of(reg_node, struct my_regulator, reg_node);
if (reg->enable_pin >= 0) {
rt_pin_mode(reg->enable_pin, PIN_MODE_OUTPUT);
rt_pin_write(reg->enable_pin,
reg->param.enable_active_high ? PIN_LOW : PIN_HIGH);
}
return RT_EOK;
}
static rt_bool_t my_regulator_is_enabled(struct rt_regulator_node *reg_node)
{
struct my_regulator *reg = rt_container_of(reg_node, struct my_regulator, reg_node);
if (reg->enable_pin >= 0) {
rt_uint8_t value;
rt_pin_mode(reg->enable_pin, PIN_MODE_INPUT);
value = rt_pin_read(reg->enable_pin);
return (value == PIN_HIGH) == reg->param.enable_active_high;
}
return RT_TRUE; /* Always on if no enable pin */
}
static int my_regulator_get_voltage(struct rt_regulator_node *reg_node)
{
struct my_regulator *reg = rt_container_of(reg_node, struct my_regulator, reg_node);
/* Fixed voltage: return average of min and max */
return (reg->param.min_uvolt + reg->param.max_uvolt) / 2;
}
static const struct rt_regulator_ops my_regulator_ops = {
.enable = my_regulator_enable,
.disable = my_regulator_disable,
.is_enabled = my_regulator_is_enabled,
.get_voltage = my_regulator_get_voltage,
};
static rt_err_t my_regulator_probe(struct rt_platform_device *pdev)
{
rt_err_t ret;
struct rt_device *dev = &pdev->parent;
struct my_regulator *reg;
rt_uint32_t voltage;
/* Allocate regulator structure */
reg = rt_calloc(1, sizeof(*reg));
if (!reg)
return -RT_ENOMEM;
/* Parse device tree properties */
if (rt_dm_dev_prop_read_u32(dev, "regulator-min-microvolt", &voltage) == 0) {
reg->param.min_uvolt = voltage;
}
if (rt_dm_dev_prop_read_u32(dev, "regulator-max-microvolt", &voltage) == 0) {
reg->param.max_uvolt = voltage;
}
reg->param.name = rt_dm_dev_get_name(dev);
reg->param.enable_active_high =
rt_dm_dev_prop_read_bool(dev, "enable-active-high");
reg->param.always_on =
rt_dm_dev_prop_read_bool(dev, "regulator-always-on");
reg->param.boot_on =
rt_dm_dev_prop_read_bool(dev, "regulator-boot-on");
/* Get enable GPIO pin */
reg->enable_pin = rt_pin_get_named_pin(dev, "enable", 0, RT_NULL, RT_NULL);
if (reg->enable_pin < 0) {
reg->enable_pin = -1; /* No GPIO control */
}
/* Apply pin control configuration */
rt_pin_ctrl_confs_apply(dev, 0);
/* Initialize regulator node */
reg->reg_node.dev = dev;
reg->reg_node.supply_name = reg->param.name;
reg->reg_node.ops = &my_regulator_ops;
reg->reg_node.param = ®->param;
/* Register with framework */
ret = rt_regulator_register(®->reg_node);
if (ret != RT_EOK) {
LOG_E("Failed to register regulator: %d", ret);
rt_free(reg);
return ret;
}
pdev->priv = reg;
LOG_I("Regulator '%s' registered: %d-%d µV",
reg->param.name, reg->param.min_uvolt, reg->param.max_uvolt);
return RT_EOK;
}
static rt_err_t my_regulator_remove(struct rt_platform_device *pdev)
{
struct my_regulator *reg = pdev->priv;
rt_regulator_unregister(®->reg_node);
rt_free(reg);
return RT_EOK;
}
static const struct rt_ofw_node_id my_regulator_ofw_ids[] = {
{ .compatible = "myvendor,my-regulator" },
{ /* sentinel */ }
};
static struct rt_platform_driver my_regulator_driver = {
.name = "my-regulator",
.ids = my_regulator_ofw_ids,
.probe = my_regulator_probe,
.remove = my_regulator_remove,
};
RT_PLATFORM_DRIVER_EXPORT(my_regulator_driver);
The framework provides a helper to parse standard regulator properties:
#ifdef RT_USING_OFW
rt_err_t regulator_ofw_parse(struct rt_ofw_node *np,
struct rt_regulator_param *param);
#endif
Usage in Driver:
#include "regulator_dm.h" /* Internal header */
static rt_err_t my_probe(struct rt_platform_device *pdev)
{
struct my_regulator *reg;
struct rt_device *dev = &pdev->parent;
reg = rt_calloc(1, sizeof(*reg));
if (!reg)
return -RT_ENOMEM;
/* Parse standard properties */
regulator_ofw_parse(dev->ofw_node, ®->param);
/* Parse custom properties */
/* ... */
return RT_EOK;
}
The regulator framework architecture with component relationships:
Handle probe deferral: Regulators may not be available during early boot
/* Good: Flexible voltage range */
ret = rt_regulator_set_voltage(reg, 1710000, 1890000);
/* Avoid: Overly strict requirement */
ret = rt_regulator_set_voltage(reg, 1800000, 1800000);
vin-supply in device tree for chained regulatorsenabled_count is managed by the framework/* Correct power-on sequence */
rt_regulator_enable(core_supply);
rt_regulator_enable(io_supply);
rt_clk_prepare_enable(clock);
/* Initialize hardware */
/* Correct power-off sequence (reverse order) */
/* Shutdown hardware */
rt_clk_disable_unprepare(clock);
rt_regulator_disable(io_supply);
rt_regulator_disable(core_supply);
/* Dynamic voltage scaling for performance modes */
switch (perf_mode) {
case PERF_HIGH:
rt_regulator_set_voltage(cpu_supply, 1200000, 1200000);
rt_clk_set_rate(cpu_clk, 1000000000); /* 1GHz */
break;
case PERF_NORMAL:
rt_clk_set_rate(cpu_clk, 800000000); /* 800MHz */
rt_regulator_set_voltage(cpu_supply, 1000000, 1000000);
break;
case PERF_LOW:
rt_clk_set_rate(cpu_clk, 400000000); /* 400MHz */
rt_regulator_set_voltage(cpu_supply, 900000, 900000);
break;
}
Regulator not found
<name>-supply property existsEnable/disable balance errors
Voltage out of range
regulator-min/max-microvolt propertiesBoot failures
regulator-boot-onEnable regulator debug logging:
#define DBG_TAG "rtdm.regulator"
#define DBG_LVL DBG_LOG
#include <rtdbg.h>
Or at runtime:
msh> ulog_tag_lvl_set rtdm.regulator 7
components/drivers/regulator/components/drivers/include/drivers/regulator.h