summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGuEe-GUI <[email protected]>2024-11-26 13:32:30 +0800
committerRbb666 <[email protected]>2024-11-27 18:02:23 +0800
commit56614a4cb94018a9dee257be4dfd9b69fd18cc46 (patch)
tree2add7a0ad0b69eb1d151e7ba0c2594ab336f9e05
parente5bc2dbdc636488802e0c367b88a6af869983e27 (diff)
[DM/FEATURE] Support regulator
1. Add the regulator driver framework. 2. Add the generic voltage and current regulator API support for drivers. Signed-off-by: GuEe-GUI <[email protected]>
-rwxr-xr-xcomponents/drivers/Kconfig1
-rw-r--r--components/drivers/include/drivers/regulator.h153
-rw-r--r--components/drivers/include/rtdevice.h4
-rw-r--r--components/drivers/regulator/Kconfig10
-rwxr-xr-xcomponents/drivers/regulator/SConscript15
-rw-r--r--components/drivers/regulator/regulator.c629
-rw-r--r--components/drivers/regulator/regulator_dm.c59
-rw-r--r--components/drivers/regulator/regulator_dm.h26
8 files changed, 897 insertions, 0 deletions
diff --git a/components/drivers/Kconfig b/components/drivers/Kconfig
index 5248fd0ff7..3c510250da 100755
--- a/components/drivers/Kconfig
+++ b/components/drivers/Kconfig
@@ -27,6 +27,7 @@ rsource "ata/Kconfig"
rsource "block/Kconfig"
rsource "nvme/Kconfig"
rsource "scsi/Kconfig"
+rsource "regulator/Kconfig"
rsource "reset/Kconfig"
rsource "virtio/Kconfig"
rsource "dma/Kconfig"
diff --git a/components/drivers/include/drivers/regulator.h b/components/drivers/include/drivers/regulator.h
new file mode 100644
index 0000000000..d74fc64277
--- /dev/null
+++ b/components/drivers/include/drivers/regulator.h
@@ -0,0 +1,153 @@
+/*
+ * Copyright (c) 2006-2023, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Change Logs:
+ * Date Author Notes
+ * 2023-09-23 GuEe-GUI first version
+ */
+
+#ifndef __REGULATOR_H__
+#define __REGULATOR_H__
+
+#include <ref.h>
+#include <rthw.h>
+#include <rtthread.h>
+
+#include <drivers/misc.h>
+
+#define RT_REGULATOR_UVOLT_INVALID (((int)(RT_UINT32_MAX >> 1)))
+
+struct rt_regulator_param
+{
+ const char *name;
+
+ int min_uvolt; /* In uV */
+ int max_uvolt; /* In uV */
+ int min_uamp; /* In uA */
+ int max_uamp; /* In uA */
+ int ramp_delay; /* In uV/usec */
+ int enable_delay; /* In usec */
+ int off_on_delay; /* In usec */
+
+ rt_uint32_t enable_active_high:1;
+ rt_uint32_t boot_on:1; /* Is enabled on boot */
+ rt_uint32_t always_on:1; /* Must be enabled */
+ rt_uint32_t soft_start:1; /* Ramp voltage slowly */
+ rt_uint32_t pull_down:1; /* Pull down resistor when regulator off */
+ rt_uint32_t over_current_protection:1; /* Auto disable on over current */
+};
+
+struct rt_regulator_ops;
+
+struct rt_regulator_node
+{
+ rt_list_t list;
+ rt_list_t children_nodes;
+
+ struct rt_device *dev;
+ struct rt_regulator_node *parent;
+
+ const char *supply_name;
+ const struct rt_regulator_ops *ops;
+
+ struct rt_ref ref;
+ rt_atomic_t enabled_count;
+
+ const struct rt_regulator_param *param;
+
+ rt_list_t notifier_nodes;
+
+ void *priv;
+};
+
+/*
+ * NOTE: Power regulator control is dangerous work. We don't want non-internal
+ * consumer could access the power regulator tree without regulator's API. So
+ * we defined the `rt_regulator` member in core instead of here.
+ */
+struct rt_regulator;
+
+#define RT_REGULATOR_MODE_INVALID 0
+#define RT_REGULATOR_MODE_FAST RT_BIT(0)
+#define RT_REGULATOR_MODE_NORMAL RT_BIT(1)
+#define RT_REGULATOR_MODE_IDLE RT_BIT(2)
+#define RT_REGULATOR_MODE_STANDBY RT_BIT(3)
+
+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);
+};
+
+struct rt_regulator_notifier;
+
+#define RT_REGULATOR_MSG_ENABLE RT_BIT(0)
+#define RT_REGULATOR_MSG_DISABLE RT_BIT(1)
+#define RT_REGULATOR_MSG_VOLTAGE_CHANGE RT_BIT(2)
+#define RT_REGULATOR_MSG_VOLTAGE_CHANGE_ERR RT_BIT(3)
+
+union rt_regulator_notifier_args
+{
+ struct
+ {
+ int old_uvolt;
+ int min_uvolt;
+ int max_uvolt;
+ };
+};
+
+typedef rt_err_t (*rt_regulator_notifier_callback)(struct rt_regulator_notifier *notifier,
+ rt_ubase_t msg, void *data);
+
+struct rt_regulator_notifier
+{
+ rt_list_t list;
+
+ struct rt_regulator *regulator;
+ rt_regulator_notifier_callback callback;
+ void *priv;
+};
+
+rt_err_t rt_regulator_register(struct rt_regulator_node *reg_np);
+rt_err_t rt_regulator_unregister(struct rt_regulator_node *reg_np);
+
+rt_err_t rt_regulator_notifier_register(struct rt_regulator *reg,
+ struct rt_regulator_notifier *notifier);
+rt_err_t rt_regulator_notifier_unregister(struct rt_regulator *reg,
+ struct rt_regulator_notifier *notifier);
+
+struct rt_regulator *rt_regulator_get(struct rt_device *dev, const char *id);
+void rt_regulator_put(struct rt_regulator *reg);
+
+rt_err_t rt_regulator_enable(struct rt_regulator *reg);
+rt_err_t rt_regulator_disable(struct rt_regulator *reg);
+rt_bool_t rt_regulator_is_enabled(struct rt_regulator *reg);
+
+rt_bool_t rt_regulator_is_supported_voltage(struct rt_regulator *reg, int min_uvolt, int max_uvolt);
+rt_err_t rt_regulator_set_voltage(struct rt_regulator *reg, int min_uvolt, int max_uvolt);
+int rt_regulator_get_voltage(struct rt_regulator *reg);
+
+rt_err_t rt_regulator_set_mode(struct rt_regulator *reg, rt_uint32_t mode);
+rt_int32_t rt_regulator_get_mode(struct rt_regulator *reg);
+
+rt_inline rt_err_t rt_regulator_set_voltage_triplet(struct rt_regulator *reg,
+ int min_uvolt, int target_uvolt, int max_uvolt)
+{
+ if (!rt_regulator_set_voltage(reg, target_uvolt, max_uvolt))
+ {
+ return RT_EOK;
+ }
+
+ return rt_regulator_set_voltage(reg, min_uvolt, max_uvolt);
+}
+
+#endif /* __REGULATOR_H__ */
diff --git a/components/drivers/include/rtdevice.h b/components/drivers/include/rtdevice.h
index ef395d3192..2e1860e00c 100644
--- a/components/drivers/include/rtdevice.h
+++ b/components/drivers/include/rtdevice.h
@@ -85,6 +85,10 @@ extern "C" {
#include "drivers/pic.h"
#endif /* RT_USING_PIC */
+#ifdef RT_USING_REGULATOR
+#include "drivers/regulator.h"
+#endif /* RT_USING_REGULATOR */
+
#ifdef RT_USING_RESET
#include "drivers/reset.h"
#endif /* RT_USING_RESET */
diff --git a/components/drivers/regulator/Kconfig b/components/drivers/regulator/Kconfig
new file mode 100644
index 0000000000..c81b4820a8
--- /dev/null
+++ b/components/drivers/regulator/Kconfig
@@ -0,0 +1,10 @@
+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
+
+if RT_USING_REGULATOR
+ osource "$(SOC_DM_REGULATOR_DIR)/Kconfig"
+endif
diff --git a/components/drivers/regulator/SConscript b/components/drivers/regulator/SConscript
new file mode 100755
index 0000000000..b5ae8e9fdc
--- /dev/null
+++ b/components/drivers/regulator/SConscript
@@ -0,0 +1,15 @@
+from building import *
+
+group = []
+
+if not GetDepend(['RT_USING_REGULATOR']):
+ Return('group')
+
+cwd = GetCurrentDir()
+CPPPATH = [cwd + '/../include']
+
+src = ['regulator.c', 'regulator_dm.c']
+
+group = DefineGroup('DeviceDrivers', src, depend = [''], CPPPATH = CPPPATH)
+
+Return('group')
diff --git a/components/drivers/regulator/regulator.c b/components/drivers/regulator/regulator.c
new file mode 100644
index 0000000000..29071e9dc0
--- /dev/null
+++ b/components/drivers/regulator/regulator.c
@@ -0,0 +1,629 @@
+/*
+ * Copyright (c) 2006-2023, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Change Logs:
+ * Date Author Notes
+ * 2023-09-23 GuEe-GUI first version
+ */
+
+#include <rtthread.h>
+#include <rtservice.h>
+
+#define DBG_TAG "rtdm.regulator"
+#define DBG_LVL DBG_INFO
+#include <rtdbg.h>
+
+#include <drivers/ofw.h>
+#include <drivers/platform.h>
+#include <drivers/regulator.h>
+
+struct rt_regulator
+{
+ struct rt_regulator_node *reg_np;
+};
+
+static struct rt_spinlock _regulator_lock = { 0 };
+
+static rt_err_t regulator_enable(struct rt_regulator_node *reg_np);
+static rt_err_t regulator_disable(struct rt_regulator_node *reg_np);
+
+rt_err_t rt_regulator_register(struct rt_regulator_node *reg_np)
+{
+ const struct rt_regulator_param *param;
+
+ if (!reg_np || !reg_np->dev || !reg_np->param || !reg_np->ops)
+ {
+ return -RT_EINVAL;
+ }
+
+ rt_list_init(&reg_np->list);
+ rt_list_init(&reg_np->children_nodes);
+ rt_list_init(&reg_np->notifier_nodes);
+ rt_ref_init(&reg_np->ref);
+ rt_atomic_store(&reg_np->enabled_count, 0);
+
+ param = reg_np->param;
+
+ reg_np->parent = RT_NULL;
+
+#ifdef RT_USING_OFW
+ if (reg_np->dev->ofw_node)
+ {
+ rt_ofw_data(reg_np->dev->ofw_node) = reg_np;
+ }
+#endif /* RT_USING_OFW */
+
+ if (param->boot_on || param->always_on)
+ {
+ regulator_enable(reg_np);
+ }
+
+ return RT_EOK;
+}
+
+rt_err_t rt_regulator_unregister(struct rt_regulator_node *reg_np)
+{
+ rt_err_t err = RT_EOK;
+
+ if (!reg_np)
+ {
+ return -RT_EINVAL;
+ }
+
+ rt_hw_spin_lock(&_regulator_lock.lock);
+
+ if (rt_atomic_load(&reg_np->enabled_count) != 0)
+ {
+ err = -RT_EBUSY;
+
+ LOG_E("%s was enabled by consumer", reg_np->supply_name);
+
+ goto _unlock;
+ }
+
+ if (!(reg_np->param->boot_on || reg_np->param->always_on))
+ {
+ regulator_disable(reg_np);
+ }
+
+ if (!rt_list_isempty(&reg_np->children_nodes) || rt_ref_read(&reg_np->ref) > 1)
+ {
+ err = -RT_EBUSY;
+
+ goto _unlock;
+ }
+
+ reg_np->parent = RT_NULL;
+ rt_list_remove(&reg_np->list);
+
+_unlock:
+ rt_hw_spin_unlock(&_regulator_lock.lock);
+
+ return err;
+}
+
+rt_err_t rt_regulator_notifier_register(struct rt_regulator *reg,
+ struct rt_regulator_notifier *notifier)
+{
+ struct rt_regulator_node *reg_np;
+
+ if (!reg || !notifier)
+ {
+ return -RT_EINVAL;
+ }
+
+ rt_hw_spin_lock(&_regulator_lock.lock);
+
+ reg_np = reg->reg_np;
+ notifier->regulator = reg;
+
+ rt_list_init(&notifier->list);
+ rt_list_insert_after(&reg_np->notifier_nodes, &notifier->list);
+
+ rt_hw_spin_unlock(&_regulator_lock.lock);
+
+ return RT_EOK;
+}
+
+rt_err_t rt_regulator_notifier_unregister(struct rt_regulator *reg,
+ struct rt_regulator_notifier *notifier)
+{
+ if (!reg || !notifier)
+ {
+ return -RT_EINVAL;
+ }
+
+ rt_hw_spin_lock(&_regulator_lock.lock);
+
+ rt_list_remove(&notifier->list);
+
+ rt_hw_spin_unlock(&_regulator_lock.lock);
+
+ return RT_EOK;
+}
+
+static rt_err_t regulator_notifier_call_chain(struct rt_regulator_node *reg_np,
+ rt_ubase_t msg, void *data)
+{
+ rt_err_t err = RT_EOK;
+ struct rt_regulator_notifier *notifier;
+ rt_list_t *head = &reg_np->notifier_nodes;
+
+ if (rt_list_isempty(head))
+ {
+ return err;
+ }
+
+ rt_list_for_each_entry(notifier, head, list)
+ {
+ err = notifier->callback(notifier, msg, data);
+
+ if (err == -RT_EIO)
+ {
+ break;
+ }
+ }
+
+ return err;
+}
+
+static rt_uint32_t regulator_get_enable_time(struct rt_regulator_node *reg_np)
+{
+ if (reg_np->param->enable_delay)
+ {
+ return reg_np->param->enable_delay;
+ }
+
+ if (reg_np->ops->enable_time)
+ {
+ return reg_np->ops->enable_time(reg_np);
+ }
+
+ return 0;
+}
+
+static void regulator_delay(rt_uint32_t delay)
+{
+ rt_uint32_t ms = delay / 1000;
+ rt_uint32_t us = delay % 1000;
+
+ if (ms > 0)
+ {
+ /*
+ * For small enough values, handle super-millisecond
+ * delays in the usleep_range() call below.
+ */
+ if (ms < 20)
+ {
+ us += ms * 1000;
+ }
+ else if (rt_thread_self())
+ {
+ rt_thread_mdelay(ms);
+ }
+ else
+ {
+ rt_hw_us_delay(ms * 1000);
+ }
+ }
+
+ /*
+ * Give the scheduler some room to coalesce with any other
+ * wakeup sources. For delays shorter than 10 us, don't even
+ * bother setting up high-resolution timers and just busy-loop.
+ */
+ if (us >= 10)
+ {
+ rt_hw_us_delay((us + 100) >> 1);
+ }
+ else
+ {
+ rt_hw_us_delay(us);
+ }
+}
+
+static rt_err_t regulator_enable(struct rt_regulator_node *reg_np)
+{
+ rt_err_t err = RT_EOK;
+ rt_uint32_t enable_delay = regulator_get_enable_time(reg_np);
+
+ if (reg_np->ops->enable)
+ {
+ err = reg_np->ops->enable(reg_np);
+
+ if (!err)
+ {
+ if (enable_delay)
+ {
+ regulator_delay(enable_delay);
+ }
+
+ rt_atomic_add(&reg_np->enabled_count, 1);
+ err = regulator_notifier_call_chain(reg_np, RT_REGULATOR_MSG_ENABLE, RT_NULL);
+ }
+ }
+
+ if (!err && reg_np->parent)
+ {
+ err = regulator_enable(reg_np->parent);
+ }
+
+ return err;
+}
+
+rt_err_t rt_regulator_enable(struct rt_regulator *reg)
+{
+ rt_err_t err;
+
+ if (!reg)
+ {
+ return -RT_EINVAL;
+ }
+
+ if (rt_regulator_is_enabled(reg))
+ {
+ return RT_EOK;
+ }
+
+ rt_hw_spin_lock(&_regulator_lock.lock);
+
+ err = regulator_enable(reg->reg_np);
+
+ rt_hw_spin_unlock(&_regulator_lock.lock);
+
+ return err;
+}
+
+static rt_err_t regulator_disable(struct rt_regulator_node *reg_np)
+{
+ rt_err_t err = RT_EOK;
+
+ if (reg_np->ops->disable)
+ {
+ err = reg_np->ops->disable(reg_np);
+
+ if (!err)
+ {
+ if (reg_np->param->off_on_delay)
+ {
+ regulator_delay(reg_np->param->off_on_delay);
+ }
+
+ err = regulator_notifier_call_chain(reg_np, RT_REGULATOR_MSG_DISABLE, RT_NULL);
+ }
+ }
+
+ if (!err && reg_np->parent)
+ {
+ err = regulator_disable(reg_np->parent);
+ }
+
+ return err;
+}
+
+rt_err_t rt_regulator_disable(struct rt_regulator *reg)
+{
+ rt_err_t err;
+
+ if (!reg)
+ {
+ return -RT_EINVAL;
+ }
+
+ if (!rt_regulator_is_enabled(reg))
+ {
+ return RT_EOK;
+ }
+
+ if (rt_atomic_load(&reg->reg_np->enabled_count) != 0)
+ {
+ rt_atomic_sub(&reg->reg_np->enabled_count, 1);
+
+ return RT_EOK;
+ }
+
+ rt_hw_spin_lock(&_regulator_lock.lock);
+
+ err = regulator_disable(reg->reg_np);
+
+ rt_hw_spin_unlock(&_regulator_lock.lock);
+
+ return err;
+}
+
+rt_bool_t rt_regulator_is_enabled(struct rt_regulator *reg)
+{
+ if (!reg)
+ {
+ return -RT_EINVAL;
+ }
+
+ if (reg->reg_np->ops->is_enabled)
+ {
+ return reg->reg_np->ops->is_enabled(reg->reg_np);
+ }
+
+ return rt_atomic_load(&reg->reg_np->enabled_count) > 0;
+}
+
+static rt_err_t regulator_set_voltage(struct rt_regulator_node *reg_np, int min_uvolt, int max_uvolt)
+{
+ rt_err_t err = RT_EOK;
+
+ if (reg_np->ops->set_voltage)
+ {
+ union rt_regulator_notifier_args args;
+
+ RT_ASSERT(reg_np->ops->get_voltage != RT_NULL);
+
+ args.old_uvolt = reg_np->ops->get_voltage(reg_np);
+ args.min_uvolt = min_uvolt;
+ args.max_uvolt = max_uvolt;
+
+ err = regulator_notifier_call_chain(reg_np, RT_REGULATOR_MSG_VOLTAGE_CHANGE, &args);
+
+ if (!err)
+ {
+ err = reg_np->ops->set_voltage(reg_np, min_uvolt, max_uvolt);
+ }
+
+ if (err)
+ {
+ regulator_notifier_call_chain(reg_np, RT_REGULATOR_MSG_VOLTAGE_CHANGE_ERR,
+ (void *)(rt_base_t)args.old_uvolt);
+ }
+ }
+
+ if (!err && reg_np->parent)
+ {
+ err = regulator_set_voltage(reg_np->parent, min_uvolt, max_uvolt);
+ }
+
+ return err;
+}
+
+rt_bool_t rt_regulator_is_supported_voltage(struct rt_regulator *reg, int min_uvolt, int max_uvolt)
+{
+ const struct rt_regulator_param *param;
+
+ RT_ASSERT(reg != RT_NULL);
+
+ param = reg->reg_np->param;
+
+ if (!param)
+ {
+ return RT_FALSE;
+ }
+
+ return param->min_uvolt <= min_uvolt && param->max_uvolt >= max_uvolt;
+}
+
+rt_err_t rt_regulator_set_voltage(struct rt_regulator *reg, int min_uvolt, int max_uvolt)
+{
+ rt_err_t err;
+
+ if (!reg)
+ {
+ return -RT_EINVAL;
+ }
+
+ rt_hw_spin_lock(&_regulator_lock.lock);
+
+ err = regulator_set_voltage(reg->reg_np, min_uvolt, max_uvolt);
+
+ rt_hw_spin_unlock(&_regulator_lock.lock);
+
+ return err;
+}
+
+int rt_regulator_get_voltage(struct rt_regulator *reg)
+{
+ int uvolt = RT_REGULATOR_UVOLT_INVALID;
+ struct rt_regulator_node *reg_np;
+
+ if (!reg)
+ {
+ return -RT_EINVAL;
+ }
+
+ rt_hw_spin_lock(&_regulator_lock.lock);
+
+ reg_np = reg->reg_np;
+
+ if (reg_np->ops->get_voltage)
+ {
+ uvolt = reg_np->ops->get_voltage(reg->reg_np);
+ }
+ else
+ {
+ uvolt = -RT_ENOSYS;
+ }
+
+ rt_hw_spin_unlock(&_regulator_lock.lock);
+
+ return uvolt;
+}
+
+rt_err_t rt_regulator_set_mode(struct rt_regulator *reg, rt_uint32_t mode)
+{
+ rt_err_t err;
+ struct rt_regulator_node *reg_np;
+
+ if (!reg)
+ {
+ return -RT_EINVAL;
+ }
+
+ rt_hw_spin_lock(&_regulator_lock.lock);
+
+ reg_np = reg->reg_np;
+
+ if (reg_np->ops->set_mode)
+ {
+ err = reg_np->ops->set_mode(reg_np, mode);
+ }
+ else
+ {
+ err = -RT_ENOSYS;
+ }
+
+ rt_hw_spin_unlock(&_regulator_lock.lock);
+
+ return err;
+}
+
+rt_int32_t rt_regulator_get_mode(struct rt_regulator *reg)
+{
+ rt_int32_t mode;
+ struct rt_regulator_node *reg_np;
+
+ if (!reg)
+ {
+ return -RT_EINVAL;
+ }
+
+ rt_hw_spin_lock(&_regulator_lock.lock);
+
+ reg_np = reg->reg_np;
+
+ if (reg_np->ops->get_mode)
+ {
+ mode = reg_np->ops->get_mode(reg_np);
+ }
+ else
+ {
+ mode = -RT_ENOSYS;
+ }
+
+ rt_hw_spin_unlock(&_regulator_lock.lock);
+
+ return mode;
+}
+
+static void regulator_check_parent(struct rt_regulator_node *reg_np)
+{
+ if (reg_np->parent)
+ {
+ return;
+ }
+ else
+ {
+ #ifdef RT_USING_OFW
+ rt_phandle parent_phandle = 0;
+ struct rt_ofw_node *np = reg_np->dev->ofw_node;
+
+ while (np)
+ {
+ if (rt_ofw_prop_read_u32(np, "vin-supply", &parent_phandle))
+ {
+ break;
+ }
+
+ if (!(np = rt_ofw_find_node_by_phandle(parent_phandle)))
+ {
+ break;
+ }
+
+ if (!(reg_np->parent = rt_ofw_data(np)))
+ {
+ LOG_W("%s parent ofw node = %s not init",
+ reg_np->supply_name, rt_ofw_node_full_name(np));
+
+ rt_ofw_node_put(np);
+ break;
+ }
+
+ rt_list_insert_after(&reg_np->parent->children_nodes, &reg_np->list);
+ rt_ofw_node_put(np);
+ }
+ #endif
+ }
+}
+
+struct rt_regulator *rt_regulator_get(struct rt_device *dev, const char *id)
+{
+ struct rt_regulator *reg = RT_NULL;
+ struct rt_regulator_node *reg_np = RT_NULL;
+
+ if (!dev || !id)
+ {
+ reg = rt_err_ptr(-RT_EINVAL);
+ goto _end;
+ }
+
+#ifdef RT_USING_OFW
+ if (dev->ofw_node)
+ {
+ rt_phandle supply_phandle;
+ struct rt_ofw_node *np = dev->ofw_node;
+ char supply_name[64];
+
+ rt_snprintf(supply_name, sizeof(supply_name), "%s-supply", id);
+
+ if (rt_ofw_prop_read_u32(np, supply_name, &supply_phandle))
+ {
+ goto _end;
+ }
+
+ if (!(np = rt_ofw_find_node_by_phandle(supply_phandle)))
+ {
+ reg = rt_err_ptr(-RT_EIO);
+ goto _end;
+ }
+
+ if (!rt_ofw_data(np))
+ {
+ rt_platform_ofw_request(np);
+ }
+
+ reg_np = rt_ofw_data(np);
+ rt_ofw_node_put(np);
+ }
+#endif
+
+ if (!reg_np)
+ {
+ reg = rt_err_ptr(-RT_ENOSYS);
+ goto _end;
+ }
+
+ rt_hw_spin_lock(&_regulator_lock.lock);
+
+ regulator_check_parent(reg_np);
+
+ rt_hw_spin_unlock(&_regulator_lock.lock);
+
+ reg = rt_calloc(1, sizeof(*reg));
+
+ if (!reg)
+ {
+ reg = rt_err_ptr(-RT_ENOMEM);
+ goto _end;
+ }
+
+ reg->reg_np = reg_np;
+ rt_ref_get(&reg_np->ref);
+
+_end:
+ return reg;
+}
+
+static void regulator_release(struct rt_ref *r)
+{
+ struct rt_regulator_node *reg_np = rt_container_of(r, struct rt_regulator_node, ref);
+
+ rt_regulator_unregister(reg_np);
+}
+
+void rt_regulator_put(struct rt_regulator *reg)
+{
+ if (!reg)
+ {
+ return;
+ }
+
+ rt_ref_put(&reg->reg_np->ref, &regulator_release);
+ rt_free(reg);
+}
diff --git a/components/drivers/regulator/regulator_dm.c b/components/drivers/regulator/regulator_dm.c
new file mode 100644
index 0000000000..d8096c43d3
--- /dev/null
+++ b/components/drivers/regulator/regulator_dm.c
@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 2006-2023, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Change Logs:
+ * Date Author Notes
+ * 2023-09-23 GuEe-GUI first version
+ */
+
+#include "regulator_dm.h"
+
+#ifdef RT_USING_OFW
+rt_err_t regulator_ofw_parse(struct rt_ofw_node *np, struct rt_regulator_param *param)
+{
+ rt_uint32_t pval;
+
+ param->name = rt_ofw_prop_read_raw(np, "regulator-name", RT_NULL);
+
+ if (!rt_ofw_prop_read_u32(np, "regulator-min-microvolt", &pval))
+ {
+ param->min_uvolt = pval;
+ }
+
+ if (!rt_ofw_prop_read_u32(np, "regulator-max-microvolt", &pval))
+ {
+ param->max_uvolt = pval;
+ }
+
+ if (!rt_ofw_prop_read_u32(np, "regulator-min-microamp", &pval))
+ {
+ param->min_uamp = pval;
+ }
+
+ if (!rt_ofw_prop_read_u32(np, "regulator-max-microamp", &pval))
+ {
+ param->max_uamp = pval;
+ }
+
+ if (!rt_ofw_prop_read_u32(np, "regulator-ramp-delay", &pval))
+ {
+ param->ramp_delay = pval;
+ }
+
+ if (!rt_ofw_prop_read_u32(np, "regulator-enable-ramp-delay", &pval))
+ {
+ param->enable_delay = pval;
+ }
+
+ param->enable_active_high = rt_ofw_prop_read_bool(np, "enable-active-high");
+ param->boot_on = rt_ofw_prop_read_bool(np, "regulator-boot-on");
+ param->always_on = rt_ofw_prop_read_bool(np, "regulator-always-on");
+ param->soft_start = rt_ofw_prop_read_bool(np, "regulator-soft-start");
+ param->pull_down = rt_ofw_prop_read_bool(np, "regulator-pull-down");
+ param->over_current_protection = rt_ofw_prop_read_bool(np, "regulator-over-current-protection");
+
+ return RT_EOK;
+}
+#endif /* RT_USING_OFW */
diff --git a/components/drivers/regulator/regulator_dm.h b/components/drivers/regulator/regulator_dm.h
new file mode 100644
index 0000000000..207bb12794
--- /dev/null
+++ b/components/drivers/regulator/regulator_dm.h
@@ -0,0 +1,26 @@
+/*
+ * Copyright (c) 2006-2023, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Change Logs:
+ * Date Author Notes
+ * 2023-09-23 GuEe-GUI first version
+ */
+
+#ifndef __REGULATOR_DM_H__
+#define __REGULATOR_DM_H__
+
+#include <rtthread.h>
+#include <rtdevice.h>
+
+#ifdef RT_USING_OFW
+rt_err_t regulator_ofw_parse(struct rt_ofw_node *np, struct rt_regulator_param *param);
+#else
+rt_inline rt_err_t regulator_ofw_parse(struct rt_ofw_node *np, struct rt_regulator_param *param);
+{
+ return RT_EOK;
+}
+#endif /* RT_USING_OFW */
+
+#endif /* __REGULATOR_DM_H__ */