diff --git a/documentation/6.components/device-driver/device_driver_model/regulator/README.md b/documentation/6.components/device-driver/device_driver_model/regulator/README.md new file mode 100644 index 0000000000..a6453a362d --- /dev/null +++ b/documentation/6.components/device-driver/device_driver_model/regulator/README.md @@ -0,0 +1,1253 @@ +# Regulator Framework + +## Introduction + +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. + +### General Overview + +Voltage regulators are electronic circuits that maintain a constant voltage level regardless of changes in load current or input voltage. They are crucial for: + +- **Power Management**: Efficient power distribution and management +- **Voltage Scaling**: Dynamic voltage and frequency scaling (DVFS) for power optimization +- **Device Protection**: Protecting devices from overvoltage and overcurrent +- **System Stability**: Ensuring stable operation of hardware components + +Common regulator types include: +- **Linear Regulators**: Simple, low-noise but less efficient +- **Switching Regulators (SMPS)**: More efficient but may generate noise +- **LDO (Low Dropout Regulators)**: Linear regulators with low voltage drop +- **Buck/Boost Converters**: Step-down/step-up switching regulators + +### RT-Thread Implementation + +The RT-Thread regulator framework, located in `components/drivers/regulator/`, provides: + +1. **Consumer API**: Simple interface for device drivers to manage power supplies +2. **Provider API**: Framework for implementing regulator drivers +3. **Regulator Tree**: Hierarchical management of regulator dependencies +4. **Device Tree Integration**: Automatic configuration from device tree +5. **Reference Counting**: Safe enable/disable with multiple consumers +6. **Notifier Chains**: Event notification for voltage changes +7. **Multiple Driver Support**: Fixed, GPIO-controlled, SCMI-based regulators + +**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) │ +└──────────────────────────────────────────────────────────┘ +``` + +## Kconfig Configuration + +### Main Configuration + +The regulator framework depends on the Device Driver Model (DM): + +```kconfig +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 first +- `RT_USING_ADT`: Abstract Data Types (automatic) +- `RT_USING_ADT_REF`: Reference counting support (automatic) + +### Regulator Driver Options + +#### Fixed Regulator +```kconfig +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 support +- `RT_USING_PINCTRL`: Pin control support + +#### GPIO Regulator +```kconfig +config 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 support + +#### SCMI Regulator +```kconfig +config 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 support +- `RT_FIRMWARE_ARM_SCMI`: ARM SCMI firmware interface + +### Vendor-Specific Options + +```kconfig +if 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. + +## Device Tree Bindings + +### Common Properties + +All regulator nodes support these standard properties: + +```dts +regulator-name = "supply_name"; /* Human-readable name */ +regulator-min-microvolt = ; /* Minimum voltage in µV */ +regulator-max-microvolt = ; /* Maximum voltage in µV */ +regulator-min-microamp = ; /* Minimum current in µA */ +regulator-max-microamp = ; /* Maximum current in µA */ +regulator-ramp-delay = ; /* Voltage change rate in µV/µs */ +regulator-enable-ramp-delay = ; /* Enable delay in µs */ +regulator-settling-time-us = ; /* Settling time in µs */ +regulator-settling-time-up-us = ; /* Settling time for voltage increase */ +regulator-settling-time-down-us = ; /* 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 Regulator Example + +Fixed regulators have a constant output voltage: + +```dts +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-Controlled Regulator Example + +GPIO regulators support multiple voltage levels: + +```dts +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>; +}; +``` + +### Consumer Usage in Device Tree + +Devices reference regulators using supply properties: + +```dts +/* 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"; +}; +``` + +## Application Layer API + +### Overview + +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. + +### Getting and Releasing Regulators + +#### rt_regulator_get + +```c +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 structure +- `id`: Supply name (matches `-supply` in device tree, e.g., "vdd", "vmmc") + +**Returns**: +- Pointer to regulator on success +- NULL on failure + +**Example**: +```c +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; +} +``` + +#### rt_regulator_put + +```c +void rt_regulator_put(struct rt_regulator *reg); +``` + +Release a regulator reference. + +**Parameters**: +- `reg`: Regulator pointer obtained from `rt_regulator_get()` + +**Example**: +```c +rt_regulator_put(vdd_reg); +``` + +### Enable and Disable + +#### rt_regulator_enable + +```c +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 pointer + +**Returns**: +- `RT_EOK` on success +- Error code on failure + +**Notes**: +- Automatically enables parent regulators +- Waits for enable delay if configured +- Calls notifier chains + +**Example**: +```c +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_regulator_disable + +```c +rt_err_t rt_regulator_disable(struct rt_regulator *reg); +``` + +Disable a regulator. Only actually disables when reference count reaches zero. + +**Parameters**: +- `reg`: Regulator pointer + +**Returns**: +- `RT_EOK` on success +- Error code on failure + +**Notes**: +- Respects `regulator-always-on` property +- Waits for off-on delay if re-enabling soon +- Calls notifier chains + +**Example**: +```c +rt_regulator_disable(vdd_reg); +``` + +#### rt_regulator_is_enabled + +```c +rt_bool_t rt_regulator_is_enabled(struct rt_regulator *reg); +``` + +Check if a regulator is currently enabled. + +**Parameters**: +- `reg`: Regulator pointer + +**Returns**: +- `RT_TRUE` if enabled +- `RT_FALSE` if disabled + +**Example**: +```c +if (rt_regulator_is_enabled(vdd_reg)) { + LOG_I("Regulator is enabled"); +} +``` + +### Voltage Control + +#### rt_regulator_set_voltage + +```c +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 pointer +- `min_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 supported +- Error code on failure + +**Notes**: +- Voltage must be within regulator's configured range +- Framework selects optimal voltage within specified range +- Waits for settling time after voltage change +- Calls notifier chains with voltage change events + +**Example**: +```c +/* 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_regulator_set_voltage_triplet + +```c +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 pointer +- `min_uvolt`: Minimum voltage in µV +- `target_uvolt`: Preferred voltage in µV +- `max_uvolt`: Maximum voltage in µV + +**Returns**: +- `RT_EOK` on success +- Error code on failure + +**Notes**: +- First attempts to set target voltage +- Falls back to min-max range if target fails + +**Example**: +```c +/* Prefer 1.8V, but accept 1.71V-1.89V */ +ret = rt_regulator_set_voltage_triplet(vdd_reg, 1710000, 1800000, 1890000); +``` + +#### rt_regulator_get_voltage + +```c +int rt_regulator_get_voltage(struct rt_regulator *reg); +``` + +Get current regulator output voltage. + +**Parameters**: +- `reg`: Regulator pointer + +**Returns**: +- Current voltage in microvolts (µV) +- Negative error code on failure + +**Example**: +```c +int voltage = rt_regulator_get_voltage(vdd_reg); +if (voltage > 0) { + LOG_I("Current voltage: %d.%03dV", voltage / 1000000, (voltage / 1000) % 1000); +} +``` + +#### rt_regulator_is_supported_voltage + +```c +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 pointer +- `min_uvolt`: Minimum voltage in µV +- `max_uvolt`: Maximum voltage in µV + +**Returns**: +- `RT_TRUE` if supported +- `RT_FALSE` if not supported + +**Example**: +```c +if (rt_regulator_is_supported_voltage(vdd_reg, 1800000, 1800000)) { + rt_regulator_set_voltage(vdd_reg, 1800000, 1800000); +} +``` + +### Operating Modes + +#### rt_regulator_set_mode + +```c +rt_err_t rt_regulator_set_mode(struct rt_regulator *reg, rt_uint32_t mode); +``` + +Set regulator operating mode. + +**Parameters**: +- `reg`: Regulator pointer +- `mode`: Operating mode flags: + - `RT_REGULATOR_MODE_FAST`: High-speed mode (higher power consumption) + - `RT_REGULATOR_MODE_NORMAL`: Normal operation mode + - `RT_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 supported +- Error code on failure + +**Example**: +```c +/* 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_regulator_get_mode + +```c +rt_int32_t rt_regulator_get_mode(struct rt_regulator *reg); +``` + +Get current regulator operating mode. + +**Parameters**: +- `reg`: Regulator pointer + +**Returns**: +- Current mode flags +- `RT_REGULATOR_MODE_INVALID` on error + +### Notification API + +#### rt_regulator_notifier_register + +```c +rt_err_t rt_regulator_notifier_register(struct rt_regulator *reg, + struct rt_regulator_notifier *notifier); +``` + +Register a notifier for regulator events. + +**Parameters**: +- `reg`: Regulator pointer +- `notifier`: Notifier structure with callback + +**Returns**: +- `RT_EOK` on success +- Error code on failure + +**Notifier Structure**: +```c +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 enabled +- `RT_REGULATOR_MSG_DISABLE`: Regulator disabled +- `RT_REGULATOR_MSG_VOLTAGE_CHANGE`: Voltage changed successfully +- `RT_REGULATOR_MSG_VOLTAGE_CHANGE_ERR`: Voltage change failed + +**Example**: +```c +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_regulator_notifier_unregister + +```c +rt_err_t rt_regulator_notifier_unregister(struct rt_regulator *reg, + struct rt_regulator_notifier *notifier); +``` + +Unregister a regulator notifier. + +**Parameters**: +- `reg`: Regulator pointer +- `notifier`: Notifier to unregister + +**Returns**: +- `RT_EOK` on success +- Error code on failure + +## Complete Application Example + +### Example: MMC/SD Driver with Regulator Control + +```c +#include +#include +#include +#include + +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); +``` + +## Driver Implementation Guide + +### Overview + +Implementing a regulator driver involves: +1. Defining regulator parameters +2. Implementing regulator operations +3. Registering with the framework +4. (Optional) Parsing device tree properties + +### Key Structures + +#### rt_regulator_node + +```c +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 */ +}; +``` + +#### rt_regulator_param + +```c +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 */ +}; +``` + +#### rt_regulator_ops + +```c +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. + +### Example: Simple Fixed Regulator Driver + +```c +#include +#include +#include +#include + +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); +``` + +### Helper Function: Device Tree Parsing + +The framework provides a helper to parse standard regulator properties: + +```c +#ifdef RT_USING_OFW +rt_err_t regulator_ofw_parse(struct rt_ofw_node *np, + struct rt_regulator_param *param); +#endif +``` + +**Usage in Driver**: +```c +#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; +} +``` + +## Architecture Diagram + +The regulator framework architecture with component relationships: + +![Regulator Architecture](regulator-architecture.svg) + +## Best Practices + +### For Consumer Drivers + +1. **Always check return values**: Regulator operations can fail +2. **Balance enable/disable**: Match every enable with a disable +3. **Order matters**: Enable regulators before clocks, disable in reverse order +4. **Use voltage ranges**: Specify acceptable voltage ranges, not exact values +5. **Handle probe deferral**: Regulators may not be available during early boot + +```c +/* Good: Flexible voltage range */ +ret = rt_regulator_set_voltage(reg, 1710000, 1890000); + +/* Avoid: Overly strict requirement */ +ret = rt_regulator_set_voltage(reg, 1800000, 1800000); +``` + +### For Provider Drivers + +1. **Implement only supported operations**: Leave unsupported ops NULL +2. **Use reference counting**: Don't track enables yourself, the framework does it +3. **Report accurate parameters**: Provide correct voltage ranges and delays +4. **Handle parent supplies**: Set `vin-supply` in device tree for chained regulators +5. **Use atomic operations carefully**: `enabled_count` is managed by the framework + +### Common Patterns + +#### Power Sequencing + +```c +/* 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); +``` + +#### Voltage Scaling + +```c +/* 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; +} +``` + +## Troubleshooting + +### Common Issues + +1. **Regulator not found** + - Check device tree: Ensure `-supply` property exists + - Check compatible string: Verify regulator driver is loaded + - Check Kconfig: Enable regulator support and driver + +2. **Enable/disable balance errors** + - Count enable/disable calls: They must match + - Check error paths: Ensure cleanup code disables on failure + - Use debugging: Add LOG_D calls to track reference count + +3. **Voltage out of range** + - Check device tree: Verify `regulator-min/max-microvolt` properties + - Check hardware: Ensure physical regulator supports the voltage + - Check parent supply: Parent must support child's voltage range + +4. **Boot failures** + - Check boot-on property: Critical regulators need `regulator-boot-on` + - Check always-on property: Some regulators must never be disabled + - Check sequencing: Power supplies must enable before consumers + +### Debug Logging + +Enable regulator debug logging: + +```c +#define DBG_TAG "rtdm.regulator" +#define DBG_LVL DBG_LOG +#include +``` + +Or at runtime: +``` +msh> ulog_tag_lvl_set rtdm.regulator 7 +``` + +## Performance Considerations + +### Memory Usage + +- Each regulator node: ~100-150 bytes +- Each consumer reference: ~16 bytes +- Device tree overhead: Depends on complexity + +### Timing Considerations + +- Enable delays: Specified by hardware (µs to ms) +- Ramp delays: Voltage-dependent (µs per volt) +- Notifier overhead: Linear with number of registered notifiers +- Lock contention: Minimal with per-regulator locking + +### Optimization Tips + +1. **Group voltage changes**: Change multiple voltages before delays +2. **Use async operations**: Don't block on regulator delays if possible +3. **Cache voltage values**: Avoid unnecessary get_voltage calls +4. **Minimize mode changes**: Mode switching can be expensive + +## Related Modules + +- **clk**: Clock management, often paired with regulator control +- **pinctrl**: Pin configuration, may require voltage switching +- **pmdomain**: Power domain management, higher-level power control +- **thermal**: Thermal management, may trigger voltage/frequency scaling + +## References + +- RT-Thread Source: `components/drivers/regulator/` +- Header File: `components/drivers/include/drivers/regulator.h` +- Device Tree Bindings: [Linux Regulator Bindings](https://www.kernel.org/doc/Documentation/devicetree/bindings/regulator/) +- [RT-Thread DM Documentation](../README.md)