summaryrefslogtreecommitdiff
path: root/components
diff options
context:
space:
mode:
authorGuEe-GUI <[email protected]>2025-12-15 12:22:24 +0800
committerR b b666 <[email protected]>2025-12-17 10:29:05 +0800
commitdf990e91d3572a6681ec763ca67ef49f5feace45 (patch)
tree473c57427d9444a3af15940f6a831f506f552036 /components
parentb4d332706ee7b75e09b19d35c205f3dc2174d5bf (diff)
[dm][power] add new drivers for power framework
1. GPIO poweroff/restart 2. Generic SYSCON regmap poweroff/reboot mode/reboot 3. Emulator battery(thermal)/charger 4. GPIO charger Signed-off-by: GuEe-GUI <[email protected]>
Diffstat (limited to 'components')
-rw-r--r--components/drivers/power/Kconfig2
-rw-r--r--components/drivers/power/SConscript11
-rw-r--r--components/drivers/power/reset/Kconfig32
-rw-r--r--components/drivers/power/reset/SConscript18
-rw-r--r--components/drivers/power/reset/gpio-poweroff.c100
-rw-r--r--components/drivers/power/reset/gpio-restart.c100
-rw-r--r--components/drivers/power/reset/reboot-mode.c105
-rw-r--r--components/drivers/power/reset/reboot-mode.h27
-rw-r--r--components/drivers/power/reset/syscon-poweroff.c104
-rw-r--r--components/drivers/power/reset/syscon-reboot-mode.c115
-rw-r--r--components/drivers/power/reset/syscon-reboot.c104
-rw-r--r--components/drivers/power/supply/Kconfig15
-rw-r--r--components/drivers/power/supply/SConscript6
-rw-r--r--components/drivers/power/supply/emu-power.c379
-rw-r--r--components/drivers/power/supply/gpio-charger.c346
15 files changed, 1464 insertions, 0 deletions
diff --git a/components/drivers/power/Kconfig b/components/drivers/power/Kconfig
new file mode 100644
index 0000000000..b832ac2350
--- /dev/null
+++ b/components/drivers/power/Kconfig
@@ -0,0 +1,2 @@
+rsource "reset/Kconfig"
+rsource "supply/Kconfig"
diff --git a/components/drivers/power/SConscript b/components/drivers/power/SConscript
new file mode 100644
index 0000000000..0b84689e91
--- /dev/null
+++ b/components/drivers/power/SConscript
@@ -0,0 +1,11 @@
+from building import *
+
+cwd = GetCurrentDir()
+objs = []
+list = os.listdir(cwd)
+
+for d in list:
+ path = os.path.join(cwd, d)
+ if os.path.isfile(os.path.join(path, 'SConscript')):
+ objs = objs + SConscript(os.path.join(d, 'SConscript'))
+Return('objs')
diff --git a/components/drivers/power/reset/Kconfig b/components/drivers/power/reset/Kconfig
index 78378fe541..376418398c 100644
--- a/components/drivers/power/reset/Kconfig
+++ b/components/drivers/power/reset/Kconfig
@@ -2,6 +2,38 @@ menuconfig RT_USING_POWER_RESET
bool "Using Board level reset or poweroff"
depends on RT_USING_DM
+config RT_POWER_RESET_GPIO_POWEROFF
+ bool "GPIO poweroff"
+ depends on RT_USING_POWER_RESET
+ depends on RT_USING_PIN
+ depends on RT_USING_PINCTRL
+
+config RT_POWER_RESET_GPIO_RESTART
+ bool "GPIO restart"
+ depends on RT_USING_POWER_RESET
+ depends on RT_USING_PIN
+ depends on RT_USING_PINCTRL
+
+config RT_POWER_RESET_SYSCON_POWEROFF
+ bool "Generic SYSCON regmap poweroff driver"
+ depends on RT_USING_POWER_RESET
+ depends on RT_MFD_SYSCON
+
+config RT_POWER_RESET_SYSCON_REBOOT_MODE
+ bool "Generic SYSCON regmap reboot mode driver"
+ depends on RT_USING_POWER_RESET
+ depends on RT_MFD_SYSCON
+ select RT_POWER_RESET_REBOOT_MODE
+
+config RT_POWER_RESET_SYSCON_REBOOT
+ bool "Generic SYSCON regmap reboot driver"
+ depends on RT_USING_POWER_RESET
+ depends on RT_MFD_SYSCON
+
if RT_USING_POWER_RESET
osource "$(SOC_DM_POWER_RESET_DIR)/Kconfig"
endif
+
+config RT_POWER_RESET_REBOOT_MODE
+ bool
+ depends on RT_USING_OFW
diff --git a/components/drivers/power/reset/SConscript b/components/drivers/power/reset/SConscript
index 9824338614..6174e407e1 100644
--- a/components/drivers/power/reset/SConscript
+++ b/components/drivers/power/reset/SConscript
@@ -10,6 +10,24 @@ CPPPATH = [cwd + '/../../include']
src = []
+if GetDepend(['RT_POWER_RESET_GPIO_POWEROFF']):
+ src += ['gpio-poweroff.c']
+
+if GetDepend(['RT_POWER_RESET_GPIO_RESTART']):
+ src += ['gpio-restart.c']
+
+if GetDepend(['RT_POWER_RESET_REBOOT_MODE']):
+ src += ['reboot-mode.c']
+
+if GetDepend(['RT_POWER_RESET_SYSCON_POWEROFF']):
+ src += ['syscon-poweroff.c']
+
+if GetDepend(['RT_POWER_RESET_SYSCON_REBOOT_MODE']):
+ src += ['syscon-reboot-mode.c']
+
+if GetDepend(['RT_POWER_RESET_SYSCON_REBOOT']):
+ src += ['syscon-reboot.c']
+
group = DefineGroup('DeviceDrivers', src, depend = [''], CPPPATH = CPPPATH)
Return('group')
diff --git a/components/drivers/power/reset/gpio-poweroff.c b/components/drivers/power/reset/gpio-poweroff.c
new file mode 100644
index 0000000000..a54a1e7f68
--- /dev/null
+++ b/components/drivers/power/reset/gpio-poweroff.c
@@ -0,0 +1,100 @@
+/*
+ * Copyright (c) 2006-2023, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Change Logs:
+ * Date Author Notes
+ * 2023-02-25 GuEe-GUI the first version
+ */
+
+#include <rtthread.h>
+#include <rtdevice.h>
+
+#define DBG_TAG "reset.gpio.poweroff"
+#define DBG_LVL DBG_INFO
+#include <rtdbg.h>
+
+struct gpio_poweroff
+{
+ rt_ubase_t pin;
+ rt_uint8_t active_value;
+
+ rt_uint32_t timeout_ms;
+ rt_uint32_t active_delay_ms;
+ rt_uint32_t inactive_delay_ms;
+};
+
+static rt_err_t gpio_poweroff_do_poweroff(struct rt_device *dev)
+{
+ struct gpio_poweroff *gp = dev->user_data;
+
+ rt_pin_mode(gp->pin, PIN_MODE_OUTPUT);
+ rt_thread_mdelay(gp->active_delay_ms);
+
+ rt_pin_write(gp->pin, !gp->active_value);
+ rt_thread_mdelay(gp->inactive_delay_ms);
+ rt_pin_write(gp->pin, gp->active_value);
+
+ rt_thread_mdelay(gp->timeout_ms);
+
+ return RT_EOK;
+}
+
+static rt_err_t gpio_poweroff_probe(struct rt_platform_device *pdev)
+{
+ rt_err_t err;
+ struct rt_device *dev = &pdev->parent;
+ struct gpio_poweroff *gp = rt_calloc(1, sizeof(*gp));
+
+ if (!gp)
+ {
+ return -RT_ENOMEM;
+ }
+
+ gp->pin = rt_pin_get_named_pin(dev, RT_NULL, 0, RT_NULL, &gp->active_value);
+
+ if (gp->pin < 0)
+ {
+ err = gp->pin;
+ goto _fail;
+ }
+
+ gp->active_delay_ms = 100;
+ gp->inactive_delay_ms = 100;
+ gp->timeout_ms = 3000;
+
+ rt_dm_dev_prop_read_u32(dev, "active-delay-ms", &gp->active_delay_ms);
+ rt_dm_dev_prop_read_u32(dev, "inactive-delay-ms", &gp->inactive_delay_ms);
+ rt_dm_dev_prop_read_u32(dev, "timeout-ms", &gp->timeout_ms);
+
+ dev->user_data = gp;
+
+ if ((err = rt_dm_power_off_handler(dev, RT_DM_POWER_OFF_MODE_SHUTDOWN,
+ RT_DM_POWER_OFF_PRIO_DEFAULT, gpio_poweroff_do_poweroff)))
+ {
+ goto _fail;
+ }
+
+ return RT_EOK;
+
+_fail:
+ rt_free(gp);
+
+ return err;
+}
+
+static const struct rt_ofw_node_id gpio_poweroff_ofw_ids[] =
+{
+ { .compatible = "gpio-poweroff" },
+ { /* sentinel */ }
+};
+
+static struct rt_platform_driver gpio_poweroff_driver =
+{
+ .name = "reset-gpio-poweroff",
+ .ids = gpio_poweroff_ofw_ids,
+
+ .probe = gpio_poweroff_probe,
+};
+RT_PLATFORM_DRIVER_EXPORT(gpio_poweroff_driver);
diff --git a/components/drivers/power/reset/gpio-restart.c b/components/drivers/power/reset/gpio-restart.c
new file mode 100644
index 0000000000..02eff0a7f4
--- /dev/null
+++ b/components/drivers/power/reset/gpio-restart.c
@@ -0,0 +1,100 @@
+/*
+ * Copyright (c) 2006-2023, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Change Logs:
+ * Date Author Notes
+ * 2023-02-25 GuEe-GUI the first version
+ */
+
+#include <rtthread.h>
+#include <rtdevice.h>
+
+#define DBG_TAG "reset.gpio.restart"
+#define DBG_LVL DBG_INFO
+#include <rtdbg.h>
+
+struct gpio_restart
+{
+ rt_ubase_t pin;
+ rt_uint8_t active_value;
+
+ rt_uint32_t wait_delay_ms;
+ rt_uint32_t active_delay_ms;
+ rt_uint32_t inactive_delay_ms;
+};
+
+static rt_err_t gpio_restart_do_restart(struct rt_device *dev)
+{
+ struct gpio_restart *gr = dev->user_data;
+
+ rt_pin_mode(gr->pin, PIN_MODE_OUTPUT);
+ rt_thread_mdelay(gr->active_delay_ms);
+
+ rt_pin_write(gr->pin, !gr->active_value);
+ rt_thread_mdelay(gr->inactive_delay_ms);
+ rt_pin_write(gr->pin, gr->active_value);
+
+ rt_thread_mdelay(gr->wait_delay_ms);
+
+ return RT_EOK;
+}
+
+static rt_err_t gpio_restart_probe(struct rt_platform_device *pdev)
+{
+ rt_err_t err;
+ struct rt_device *dev = &pdev->parent;
+ struct gpio_restart *gr = rt_calloc(1, sizeof(*gr));
+
+ if (!gr)
+ {
+ return -RT_ENOMEM;
+ }
+
+ gr->pin = rt_pin_get_named_pin(dev, RT_NULL, 0, RT_NULL, &gr->active_value);
+
+ if (gr->pin < 0)
+ {
+ err = gr->pin;
+ goto _fail;
+ }
+
+ gr->active_delay_ms = 100;
+ gr->inactive_delay_ms = 100;
+ gr->wait_delay_ms = 3000;
+
+ rt_dm_dev_prop_read_u32(dev, "active-delay", &gr->active_delay_ms);
+ rt_dm_dev_prop_read_u32(dev, "inactive-delay", &gr->inactive_delay_ms);
+ rt_dm_dev_prop_read_u32(dev, "wait-delay", &gr->wait_delay_ms);
+
+ dev->user_data = gr;
+
+ if ((err = rt_dm_power_off_handler(dev, RT_DM_POWER_OFF_MODE_RESET,
+ RT_DM_POWER_OFF_PRIO_DEFAULT, gpio_restart_do_restart)))
+ {
+ goto _fail;
+ }
+
+ return RT_EOK;
+
+_fail:
+ rt_free(gr);
+
+ return err;
+}
+
+static const struct rt_ofw_node_id gpio_restart_ofw_ids[] =
+{
+ { .compatible = "gpio-restart" },
+ { /* sentinel */ }
+};
+
+static struct rt_platform_driver gpio_restart_driver =
+{
+ .name = "reset-gpio-restart",
+ .ids = gpio_restart_ofw_ids,
+
+ .probe = gpio_restart_probe,
+};
+RT_PLATFORM_DRIVER_EXPORT(gpio_restart_driver);
diff --git a/components/drivers/power/reset/reboot-mode.c b/components/drivers/power/reset/reboot-mode.c
new file mode 100644
index 0000000000..d2a65f53fb
--- /dev/null
+++ b/components/drivers/power/reset/reboot-mode.c
@@ -0,0 +1,105 @@
+/*
+ * Copyright (c) 2006-2023, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Change Logs:
+ * Date Author Notes
+ * 2023-02-25 GuEe-GUI the first version
+ */
+
+#include "reboot-mode.h"
+
+#define MODE_SUFFIXE "mode-"
+
+struct mode_info
+{
+ rt_slist_t list;
+
+ const char *mode;
+ rt_uint32_t magic;
+};
+
+static rt_err_t reboot_mode_work(struct rt_device *dev, char *cmd)
+{
+ struct mode_info *info;
+ struct reboot_mode *reboot = (void *)dev;
+
+ cmd = cmd ? : "normal";
+
+ rt_slist_for_each_entry(info, &reboot->mode_nodes, list)
+ {
+ if (!rt_strcmp(info->mode, cmd))
+ {
+ reboot->write(reboot, info->magic);
+ break;
+ }
+ }
+
+ return RT_EOK;
+}
+
+rt_err_t reboot_mode_register(struct reboot_mode *reboot)
+{
+ rt_err_t err;
+ struct mode_info *info;
+ struct rt_ofw_prop *prop;
+ struct rt_ofw_node *np = reboot->dev->ofw_node;
+ const int mode_suffixe_len = sizeof(MODE_SUFFIXE) - 1;
+
+ if (!reboot || !reboot->dev)
+ {
+ return -RT_EINVAL;
+ }
+
+ rt_slist_init(&reboot->mode_nodes);
+
+ rt_ofw_foreach_prop(np, prop)
+ {
+ if (rt_strncmp(prop->name, MODE_SUFFIXE, mode_suffixe_len))
+ {
+ continue;
+ }
+
+ info = rt_malloc(sizeof(*info));
+
+ if (!info)
+ {
+ err = -RT_ENOMEM;
+
+ goto _end;
+ }
+
+ info->mode = prop->name + mode_suffixe_len;
+ info->magic = fdt32_to_cpu(*(const fdt32_t *)prop->value);
+
+ rt_slist_init(&info->list);
+
+ rt_slist_insert(&reboot->mode_nodes, &info->list);
+ }
+
+ err = rt_dm_reboot_mode_register((void *)reboot, &reboot_mode_work);
+
+_end:
+ if (err)
+ {
+ struct mode_info *prev_info = RT_NULL;
+
+ rt_slist_for_each_entry(info, &reboot->mode_nodes, list)
+ {
+ if (prev_info)
+ {
+ rt_free(prev_info);
+ }
+
+ prev_info = info;
+ }
+
+ if (prev_info)
+ {
+ rt_free(prev_info);
+ }
+ }
+
+ return err;
+}
diff --git a/components/drivers/power/reset/reboot-mode.h b/components/drivers/power/reset/reboot-mode.h
new file mode 100644
index 0000000000..3035882036
--- /dev/null
+++ b/components/drivers/power/reset/reboot-mode.h
@@ -0,0 +1,27 @@
+/*
+ * Copyright (c) 2006-2023, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Change Logs:
+ * Date Author Notes
+ * 2023-02-25 GuEe-GUI the first version
+ */
+
+#ifndef __RESET_REBOOT_MODE_H__
+#define __RESET_REBOOT_MODE_H__
+
+#include <rtthread.h>
+#include <rtdevice.h>
+
+struct reboot_mode
+{
+ rt_slist_t mode_nodes;
+
+ struct rt_device *dev;
+ rt_err_t (*write)(struct reboot_mode *reboot, rt_uint32_t magic);
+};
+
+rt_err_t reboot_mode_register(struct reboot_mode *reboot);
+
+#endif /* __RESET_REBOOT_MODE_H__ */
diff --git a/components/drivers/power/reset/syscon-poweroff.c b/components/drivers/power/reset/syscon-poweroff.c
new file mode 100644
index 0000000000..aaa8d25b6e
--- /dev/null
+++ b/components/drivers/power/reset/syscon-poweroff.c
@@ -0,0 +1,104 @@
+/*
+ * Copyright (c) 2006-2023, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Change Logs:
+ * Date Author Notes
+ * 2023-02-25 GuEe-GUI the first version
+ */
+
+#include <rtthread.h>
+#include <rtdevice.h>
+
+#define DBG_TAG "reset.syscon.poweroff"
+#define DBG_LVL DBG_INFO
+#include <rtdbg.h>
+
+static struct rt_syscon *syscon;
+static rt_uint32_t offset, value, mask;
+
+static void syscon_poweroff(void)
+{
+ /* Issue the poweroff */
+ rt_syscon_update_bits(syscon, offset, mask, value);
+
+ rt_thread_mdelay(1000);
+
+ LOG_E("Unable to poweroff system");
+}
+
+static rt_err_t syscon_poweroff_probe(struct rt_platform_device *pdev)
+{
+ rt_err_t mask_err, value_err;
+ struct rt_ofw_node *np = pdev->parent.ofw_node;
+
+ syscon = rt_syscon_find_by_ofw_phandle(np, "regmap");
+
+ if (!syscon)
+ {
+ return -RT_ERROR;
+ }
+
+ if (rt_ofw_prop_read_u32(np, "offset", &offset))
+ {
+ LOG_E("read '%s' fail", "offset");
+
+ return -RT_EINVAL;
+ }
+
+ value_err = rt_ofw_prop_read_u32(np, "value", &value);
+ mask_err = rt_ofw_prop_read_u32(np, "mask", &mask);
+
+ if (value_err && mask_err)
+ {
+ LOG_E("read '%s' and '%s' fail", "value", "mask");
+
+ return -RT_EINVAL;
+ }
+
+ if (value_err)
+ {
+ /* support old binding */
+ value = mask;
+ mask = 0xffffffff;
+ }
+ else if (mask_err)
+ {
+ /* support value without mask */
+ mask = 0xffffffff;
+ }
+
+ if (rt_dm_machine_shutdown)
+ {
+ LOG_E("rt_dm_machine_shutdown have hook %p", rt_dm_machine_shutdown);
+
+ return -RT_EBUSY;
+ }
+
+ rt_dm_machine_shutdown = syscon_poweroff;
+
+ return RT_EOK;
+}
+
+static const struct rt_ofw_node_id syscon_poweroff_ofw_ids[] =
+{
+ { .compatible = "syscon-poweroff" },
+ { /* sentinel */ }
+};
+
+static struct rt_platform_driver syscon_poweroff_driver =
+{
+ .name = "reset-syscon-poweroff",
+ .ids = syscon_poweroff_ofw_ids,
+
+ .probe = syscon_poweroff_probe,
+};
+
+static int syscon_poweroff_driver_register(void)
+{
+ rt_platform_driver_register(&syscon_poweroff_driver);
+
+ return 0;
+}
+INIT_SUBSYS_EXPORT(syscon_poweroff_driver_register);
diff --git a/components/drivers/power/reset/syscon-reboot-mode.c b/components/drivers/power/reset/syscon-reboot-mode.c
new file mode 100644
index 0000000000..da3afc7441
--- /dev/null
+++ b/components/drivers/power/reset/syscon-reboot-mode.c
@@ -0,0 +1,115 @@
+/*
+ * Copyright (c) 2006-2023, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Change Logs:
+ * Date Author Notes
+ * 2023-02-25 GuEe-GUI the first version
+ */
+
+#include <rtthread.h>
+#include <rtdevice.h>
+
+#define DBG_TAG "reset.syscon.reboot-mode"
+#define DBG_LVL DBG_INFO
+#include <rtdbg.h>
+
+#include "reboot-mode.h"
+
+struct syscon_reboot_mode
+{
+ struct rt_syscon *map;
+
+ struct reboot_mode reboot;
+
+ rt_uint32_t offset;
+ rt_uint32_t mask;
+};
+
+static rt_err_t syscon_reboot_mode_write(struct reboot_mode *reboot,
+ rt_uint32_t magic)
+{
+ rt_err_t err;
+ struct syscon_reboot_mode *srbm;
+
+ srbm = rt_container_of(reboot, struct syscon_reboot_mode, reboot);
+
+ err = rt_syscon_update_bits(srbm->map, srbm->offset, srbm->mask, magic);
+
+ if (err)
+ {
+ LOG_E("Update reboot mode bits failed");
+ }
+
+ return err;
+}
+
+static rt_err_t syscon_reboot_mode_probe(struct rt_platform_device *pdev)
+{
+ rt_err_t err;
+ struct rt_ofw_node *np;
+ struct rt_device *dev = &pdev->parent;
+ struct syscon_reboot_mode *srbm = rt_calloc(1, sizeof(*srbm));
+
+ if (!srbm)
+ {
+ return -RT_ENOMEM;
+ }
+
+ np = rt_ofw_get_parent(dev->ofw_node);
+ srbm->map = rt_syscon_find_by_ofw_node(np);
+ rt_ofw_node_put(np);
+
+ if (!srbm->map)
+ {
+ err = -RT_EIO;
+ goto _fail;
+ }
+
+ srbm->reboot.dev = dev;
+ srbm->reboot.write = syscon_reboot_mode_write;
+ srbm->mask = 0xffffffff;
+
+ if (rt_dm_dev_prop_read_u32(dev, "offset", &srbm->offset))
+ {
+ err = -RT_EINVAL;
+ goto _fail;
+ }
+
+ rt_dm_dev_prop_read_u32(dev, "mask", &srbm->mask);
+
+ if ((err = reboot_mode_register(&srbm->reboot)))
+ {
+ goto _fail;
+ }
+
+ return RT_EOK;
+
+_fail:
+ rt_free(srbm);
+
+ return err;
+}
+
+static const struct rt_ofw_node_id syscon_reboot_mode_ofw_ids[] =
+{
+ { .compatible = "syscon-reboot-mode" },
+ { /* sentinel */ }
+};
+
+static struct rt_platform_driver syscon_reboot_mode_driver =
+{
+ .name = "reset-syscon-reboot-mode",
+ .ids = syscon_reboot_mode_ofw_ids,
+
+ .probe = syscon_reboot_mode_probe,
+};
+
+static int syscon_reboot_mode_driver_register(void)
+{
+ rt_platform_driver_register(&syscon_reboot_mode_driver);
+
+ return 0;
+}
+INIT_SUBSYS_EXPORT(syscon_reboot_mode_driver_register);
diff --git a/components/drivers/power/reset/syscon-reboot.c b/components/drivers/power/reset/syscon-reboot.c
new file mode 100644
index 0000000000..b4151b4d78
--- /dev/null
+++ b/components/drivers/power/reset/syscon-reboot.c
@@ -0,0 +1,104 @@
+/*
+ * Copyright (c) 2006-2023, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Change Logs:
+ * Date Author Notes
+ * 2023-02-25 GuEe-GUI the first version
+ */
+
+#include <rtthread.h>
+#include <rtdevice.h>
+
+#define DBG_TAG "reset.syscon.reboot"
+#define DBG_LVL DBG_INFO
+#include <rtdbg.h>
+
+static struct rt_syscon *syscon;
+static rt_uint32_t offset, value, mask;
+
+static void syscon_reboot(void)
+{
+ /* Issue the reboot */
+ rt_syscon_update_bits(syscon, offset, mask, value);
+
+ rt_thread_mdelay(1000);
+
+ LOG_E("Unable to restart system");
+}
+
+static rt_err_t syscon_reboot_probe(struct rt_platform_device *pdev)
+{
+ rt_err_t mask_err, value_err;
+ struct rt_ofw_node *np = pdev->parent.ofw_node;
+
+ syscon = rt_syscon_find_by_ofw_phandle(np, "regmap");
+
+ if (!syscon)
+ {
+ return -RT_ERROR;
+ }
+
+ if (rt_ofw_prop_read_u32(np, "offset", &offset))
+ {
+ LOG_E("read '%s' fail", "offset");
+
+ return -RT_EINVAL;
+ }
+
+ value_err = rt_ofw_prop_read_u32(np, "value", &value);
+ mask_err = rt_ofw_prop_read_u32(np, "mask", &mask);
+
+ if (value_err && mask_err)
+ {
+ LOG_E("read '%s' and '%s' fail", "value", "mask");
+
+ return -RT_EINVAL;
+ }
+
+ if (value_err)
+ {
+ /* support old binding */
+ value = mask;
+ mask = 0xffffffff;
+ }
+ else if (mask_err)
+ {
+ /* support value without mask */
+ mask = 0xffffffff;
+ }
+
+ if (rt_dm_machine_reset)
+ {
+ LOG_E("rt_dm_machine_reset have hook %p", rt_dm_machine_reset);
+
+ return -RT_EBUSY;
+ }
+
+ rt_dm_machine_reset = syscon_reboot;
+
+ return RT_EOK;
+}
+
+static const struct rt_ofw_node_id syscon_reboot_ofw_ids[] =
+{
+ { .compatible = "syscon-reboot" },
+ { /* sentinel */ }
+};
+
+static struct rt_platform_driver syscon_reboot_driver =
+{
+ .name = "reset-syscon-reboot",
+ .ids = syscon_reboot_ofw_ids,
+
+ .probe = syscon_reboot_probe,
+};
+
+static int syscon_reboot_driver_register(void)
+{
+ rt_platform_driver_register(&syscon_reboot_driver);
+
+ return 0;
+}
+INIT_SUBSYS_EXPORT(syscon_reboot_driver_register);
diff --git a/components/drivers/power/supply/Kconfig b/components/drivers/power/supply/Kconfig
index be78289018..b03ffa1e05 100644
--- a/components/drivers/power/supply/Kconfig
+++ b/components/drivers/power/supply/Kconfig
@@ -15,6 +15,21 @@ if RT_USING_POWER_SUPPLY
comment "Power Supply Device Drivers"
endif
+config RT_POWER_SUPPLY_EMU
+ bool "Emulator battery(thermal)/charger"
+ depends on RT_USING_POWER_SUPPLY
+ depends on RT_USING_PM
+ depends on RT_USING_CONSOLE
+ depends on RT_USING_MSH
+ depends on RT_USING_CPU_USAGE_TRACER
+ default n
+
+config RT_POWER_SUPPLY_CHARGER_GPIO
+ bool "GPIO charger"
+ depends on RT_USING_POWER_SUPPLY
+ depends on RT_USING_PIN
+ default y
+
if RT_USING_POWER_SUPPLY
osource "$(SOC_DM_POWER_SUPPLY_DIR)/Kconfig"
endif
diff --git a/components/drivers/power/supply/SConscript b/components/drivers/power/supply/SConscript
index a8c082c0db..5039b34f04 100644
--- a/components/drivers/power/supply/SConscript
+++ b/components/drivers/power/supply/SConscript
@@ -13,6 +13,12 @@ src = ['supply.c']
if GetDepend(['RT_POWER_SUPPLY_DAEMON']):
src += ['supply-daemon.c']
+if GetDepend(['RT_POWER_SUPPLY_EMU']):
+ src += ['emu-power.c']
+
+if GetDepend(['RT_POWER_SUPPLY_CHARGER_GPIO']):
+ src += ['gpio-charger.c']
+
group = DefineGroup('DeviceDrivers', src, depend = [''], CPPPATH = CPPPATH)
Return('group')
diff --git a/components/drivers/power/supply/emu-power.c b/components/drivers/power/supply/emu-power.c
new file mode 100644
index 0000000000..f28438cd92
--- /dev/null
+++ b/components/drivers/power/supply/emu-power.c
@@ -0,0 +1,379 @@
+/*
+ * Copyright (c) 2006-2023, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Change Logs:
+ * Date Author Notes
+ * 2023-02-25 GuEe-GUI the first version
+ */
+
+#include <rtthread.h>
+#include <rtdevice.h>
+#include <drivers/misc.h>
+
+#define POLL_INTERVAL_MS 3000
+#define CHARGE_STEP_FAST 4
+#define CHARGE_STEP_NORMAL 2
+#define CHARGE_STEP_TRICKLE 1
+#define DISCHARGE_BASE_RATE 1
+#define TEMP_SENSITIVITY 30
+
+struct emu_power
+{
+ struct rt_device parent;
+
+ struct rt_power_supply battery;
+ struct rt_power_supply charger;
+
+ struct rt_timer poller;
+
+ rt_uint32_t status;
+ rt_uint32_t health;
+ rt_uint32_t present;
+ rt_uint32_t capacity;
+ rt_uint32_t voltage;
+ rt_uint32_t temp;
+ rt_uint32_t charge_counter;
+ rt_uint32_t current_now;
+ rt_uint32_t current_avg;
+ rt_uint32_t charge_full_uah;
+ rt_uint32_t cycle_count;
+ rt_uint32_t ac_online;
+ rt_uint32_t voltage_max;
+ rt_uint32_t current_max;
+
+ rt_tick_t last_poll_tick;
+
+ rt_uint8_t load_index;
+ rt_ubase_t cpu_load;
+ rt_ubase_t load_history[5];
+ rt_ubase_t last_idle;
+ rt_ubase_t last_total;
+};
+static struct emu_power _emu_power;
+
+static enum rt_power_supply_property emu_battery_properties[] =
+{
+ RT_POWER_SUPPLY_PROP_STATUS,
+ RT_POWER_SUPPLY_PROP_HEALTH,
+ RT_POWER_SUPPLY_PROP_PRESENT,
+ RT_POWER_SUPPLY_PROP_TECHNOLOGY,
+ RT_POWER_SUPPLY_PROP_CAPACITY,
+ RT_POWER_SUPPLY_PROP_VOLTAGE_NOW,
+ RT_POWER_SUPPLY_PROP_TEMP,
+ RT_POWER_SUPPLY_PROP_CHARGE_COUNTER,
+ RT_POWER_SUPPLY_PROP_CURRENT_NOW,
+ RT_POWER_SUPPLY_PROP_CURRENT_AVG,
+ RT_POWER_SUPPLY_PROP_CHARGE_FULL,
+ RT_POWER_SUPPLY_PROP_CYCLE_COUNT,
+ RT_POWER_SUPPLY_PROP_SCOPE,
+};
+
+static struct rt_power_supply_battery_info emu_battery_info =
+{
+ .technology = RT_POWER_SUPPLY_TECHNOLOGY_LION,
+ .energy_full_design_uwh = 3000000000, /* 3000mWh */
+ .charge_full_design_uah = 3000000, /* 3000mAh */
+ .voltage_min_design_uv = 3000000, /* 3.0V */
+ .voltage_max_design_uv = 4200000, /* 4.2V */
+ .precharge_current_ua = 500000, /* 500mA */
+ .charge_term_current_ua = 1000000, /* 1000mA */
+ .charge_restart_voltage_uv = 3500000, /* 3.5V */
+ .constant_charge_current_max_ua = 2000000, /* 2000mA */
+ .constant_charge_voltage_max_uv = 4200000, /* 4.2V */
+ .temp_ambient_alert_min = -10000, /* -10C */
+ .temp_ambient_alert_max = 40000, /* 40C */
+ .temp_alert_min = 20000, /* 20C */
+ .temp_alert_max = 25000, /* 25C */
+ .temp_min = 0, /* 0C */
+ .temp_max = 35000, /* 35C */
+};
+
+static rt_err_t emu_battery_get_property(struct rt_power_supply *psy,
+ enum rt_power_supply_property prop, union rt_power_supply_property_val *val)
+{
+ struct emu_power *ep = rt_container_of(psy, struct emu_power, battery);
+
+ switch (prop)
+ {
+ case RT_POWER_SUPPLY_PROP_STATUS:
+ val->intval = ep->status;
+ break;
+
+ case RT_POWER_SUPPLY_PROP_HEALTH:
+ val->intval = ep->health;
+ break;
+
+ case RT_POWER_SUPPLY_PROP_PRESENT:
+ val->intval = ep->present;
+ break;
+
+ case RT_POWER_SUPPLY_PROP_TECHNOLOGY:
+ val->intval = RT_POWER_SUPPLY_TECHNOLOGY_LION;
+ break;
+
+ case RT_POWER_SUPPLY_PROP_CAPACITY:
+ val->intval = ep->capacity;
+ break;
+
+ case RT_POWER_SUPPLY_PROP_VOLTAGE_NOW:
+ val->intval = ep->voltage;
+ break;
+
+ case RT_POWER_SUPPLY_PROP_TEMP:
+ val->intval = ep->temp;
+ break;
+
+ case RT_POWER_SUPPLY_PROP_CHARGE_COUNTER:
+ val->intval = ep->charge_counter;
+ break;
+
+ case RT_POWER_SUPPLY_PROP_CURRENT_NOW:
+ val->intval = ep->current_now;
+ break;
+
+ case RT_POWER_SUPPLY_PROP_CURRENT_AVG:
+ val->intval = ep->current_avg;
+ break;
+
+ case RT_POWER_SUPPLY_PROP_CHARGE_FULL:
+ val->intval = ep->charge_full_uah;
+ break;
+
+ case RT_POWER_SUPPLY_PROP_CYCLE_COUNT:
+ val->intval = ep->cycle_count;
+ break;
+
+ case RT_POWER_SUPPLY_PROP_SCOPE:
+ val->intval = RT_POWER_SUPPLY_SCOPE_SYSTEM;
+ break;
+
+ default:
+ return -RT_EINVAL;
+ }
+
+ return RT_EOK;
+}
+
+static const struct rt_power_supply_ops emu_battery_ops =
+{
+ .get_property = emu_battery_get_property,
+};
+
+static enum rt_power_supply_property emu_charger_properties[] =
+{
+ RT_POWER_SUPPLY_PROP_ONLINE,
+ RT_POWER_SUPPLY_PROP_VOLTAGE_MAX,
+ RT_POWER_SUPPLY_PROP_CURRENT_MAX,
+};
+
+static rt_err_t emu_charger_get_property(struct rt_power_supply *psy,
+ enum rt_power_supply_property prop, union rt_power_supply_property_val *val)
+{
+ struct emu_power *ep = rt_container_of(psy, struct emu_power, charger);
+
+ switch (prop)
+ {
+ case RT_POWER_SUPPLY_PROP_ONLINE:
+ val->intval = ep->ac_online;
+ break;
+
+ case RT_POWER_SUPPLY_PROP_VOLTAGE_MAX:
+ val->intval = ep->voltage_max;
+ break;
+
+ case RT_POWER_SUPPLY_PROP_CURRENT_MAX:
+ val->intval = ep->current_max;
+ break;
+
+ default:
+ return -RT_ENOSYS;
+ }
+
+ return RT_EOK;
+}
+
+static const struct rt_power_supply_ops emu_charger_ops =
+{
+ .get_property = emu_charger_get_property,
+};
+
+static void emu_power_poll(void *param)
+{
+ rt_tick_t current_tick;
+ rt_uint32_t elapsed_ms, avg_load = 0;
+ rt_ubase_t current_idle, current_total, idle_diff, total_diff;
+ struct rt_cpu_usage_stats *stats = &rt_cpu_self()->cpu_stat;
+ struct emu_power *ep = param;
+
+ current_tick = rt_tick_get();
+ elapsed_ms = (current_tick - ep->last_poll_tick) * (1000 / RT_TICK_PER_SECOND);
+ ep->last_poll_tick = current_tick;
+
+ current_idle = stats->idle;
+ current_total = stats->user + stats->system + stats->irq + current_idle;
+
+ idle_diff = current_idle - ep->last_idle;
+ total_diff = current_total - ep->last_total;
+
+ ep->last_idle = current_idle;
+ ep->last_total = current_total;
+
+ if (total_diff > 0)
+ {
+ ep->cpu_load = 100 - (idle_diff * 100) / total_diff;
+ }
+ else
+ {
+ ep->cpu_load = 0;
+ }
+ ep->cpu_load = rt_clamp((rt_ubase_t)ep->cpu_load, 0UL, 100UL);
+
+ ep->load_history[ep->load_index++ % RT_ARRAY_SIZE(ep->load_history)] = ep->cpu_load;
+
+ for (int i = 0; i < RT_ARRAY_SIZE(ep->load_history); i++)
+ {
+ avg_load += ep->load_history[i];
+ }
+ avg_load /= RT_ARRAY_SIZE(ep->load_history);
+
+ if (ep->ac_online)
+ {
+ int step;
+
+ if (ep->capacity < 80)
+ {
+ step = CHARGE_STEP_FAST;
+ ep->current_now = 2000000;
+ }
+ else if (ep->capacity < 95)
+ {
+ step = CHARGE_STEP_NORMAL;
+ ep->current_now = 1000000;
+ }
+ else
+ {
+ step = CHARGE_STEP_TRICKLE;
+ ep->current_now = 500000;
+ }
+
+ ep->capacity = rt_min_t(rt_uint32_t,
+ ep->capacity + (step * elapsed_ms) / POLL_INTERVAL_MS, 100);
+
+ ep->voltage = emu_battery_info.voltage_max_design_uv - (100 - ep->capacity) * 30;
+
+ if (ep->capacity >= 100)
+ {
+ ep->status = RT_POWER_SUPPLY_STATUS_FULL;
+ }
+ else
+ {
+ ep->status = RT_POWER_SUPPLY_STATUS_CHARGING;
+ }
+ }
+ else
+ {
+ int drain = (avg_load * DISCHARGE_BASE_RATE * elapsed_ms) / 1000;
+
+ ep->capacity = rt_max_t(rt_uint32_t, ep->capacity - drain, 0);
+ ep->current_now = -(500000 + (avg_load * 5000));
+ ep->voltage = emu_battery_info.voltage_min_design_uv + ep->capacity * 1200;
+
+ ep->status = (ep->capacity > 0) ?
+ RT_POWER_SUPPLY_STATUS_DISCHARGING : RT_POWER_SUPPLY_STATUS_NOT_CHARGING;
+ }
+
+ ep->temp = 25000 + (ep->current_now / 1000) * TEMP_SENSITIVITY;
+ ep->temp = rt_clamp((rt_uint32_t)ep->temp,
+ (rt_uint32_t)emu_battery_info.temp_min, (rt_uint32_t)emu_battery_info.temp_max);
+
+ rt_power_supply_changed(&ep->charger);
+ rt_power_supply_changed(&ep->battery);
+}
+
+static int emu_power_init(void)
+{
+ struct rt_cpu_usage_stats *stats = &rt_cpu_self()->cpu_stat;
+ struct emu_power *ep = &_emu_power;
+
+ rt_memset(ep, 0, sizeof(*ep));
+
+ rt_dm_dev_set_name(&ep->parent, "emu-power");
+
+ ep->battery.dev = &ep->parent,
+ ep->battery.type = RT_POWER_SUPPLY_TYPE_BATTERY,
+ ep->battery.properties_nr = RT_ARRAY_SIZE(emu_battery_properties),
+ ep->battery.properties = emu_battery_properties,
+ ep->battery.battery_info = &emu_battery_info,
+ ep->battery.ops = &emu_battery_ops,
+
+ ep->charger.dev = &ep->parent,
+ ep->charger.type = RT_POWER_SUPPLY_TYPE_USB_SDP,
+ ep->charger.properties_nr = RT_ARRAY_SIZE(emu_charger_properties),
+ ep->charger.properties = emu_charger_properties,
+ ep->charger.ops = &emu_charger_ops,
+
+ ep->status = RT_POWER_SUPPLY_STATUS_DISCHARGING;
+ ep->health = RT_POWER_SUPPLY_HEALTH_GOOD;
+ ep->present = 1;
+ ep->capacity = 100;
+ ep->voltage = 3800000;
+ ep->voltage_max = emu_battery_info.voltage_max_design_uv;
+ ep->current_max = emu_battery_info.constant_charge_current_max_ua;
+ ep->temp = 25000;
+ ep->last_poll_tick = rt_tick_get();
+ ep->last_idle = stats->idle;
+ ep->last_total = stats->user + stats->system + stats->irq + stats->idle;
+
+ rt_power_supply_register(&ep->battery);
+ rt_power_supply_register(&ep->charger);
+
+ rt_timer_init(&ep->poller, ep->parent.parent.name, &emu_power_poll, ep,
+ rt_tick_from_millisecond(POLL_INTERVAL_MS), RT_TIMER_FLAG_PERIODIC);
+ rt_timer_start(&ep->poller);
+
+ return 0;
+}
+INIT_DEVICE_EXPORT(emu_power_init);
+
+static int emu_charger(int argc, char**argv)
+{
+ rt_base_t level;
+ struct emu_power *ep = &_emu_power;
+
+ if (argc != 2)
+ {
+ goto _help;
+ }
+
+ level = rt_hw_interrupt_disable();
+
+ if (!rt_strcmp(argv[1], "on"))
+ {
+ ep->status = RT_POWER_SUPPLY_STATUS_CHARGING;
+ ep->ac_online = 1;
+ ep->current_max = emu_battery_info.constant_charge_current_max_ua;
+ }
+ else if (!rt_strcmp(argv[1], "off"))
+ {
+ ep->status = RT_POWER_SUPPLY_STATUS_DISCHARGING;
+ ep->ac_online = 0;
+ ep->current_max = 0;
+ }
+ else
+ {
+ rt_hw_interrupt_enable(level);
+ goto _help;
+ }
+
+ rt_hw_interrupt_enable(level);
+
+ return 0;
+
+_help:
+ rt_kprintf("Usage: %s [on|off]\n", __func__);
+
+ return -1;
+}
+MSH_CMD_EXPORT(emu_charger, emu charger switch);
diff --git a/components/drivers/power/supply/gpio-charger.c b/components/drivers/power/supply/gpio-charger.c
new file mode 100644
index 0000000000..1843a959a4
--- /dev/null
+++ b/components/drivers/power/supply/gpio-charger.c
@@ -0,0 +1,346 @@
+/*
+ * Copyright (c) 2006-2023, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Change Logs:
+ * Date Author Notes
+ * 2023-02-25 GuEe-GUI the first version
+ */
+
+#include <rtthread.h>
+#include <rtdevice.h>
+
+struct gpio_desc
+{
+ rt_base_t pin;
+ rt_uint8_t flags;
+};
+
+rt_packed(struct gpio_mapping
+{
+ rt_uint32_t limit_ua;
+ rt_uint32_t gpiodata;
+});
+
+struct gpio_charger
+{
+ struct rt_power_supply parent;
+
+ struct gpio_desc gpiod;
+ struct gpio_desc charge_status;
+
+ rt_ssize_t current_limit_gpios_nr;
+ struct gpio_desc *current_limit_gpios;
+ struct gpio_mapping *current_limit_map;
+
+ rt_uint32_t current_limit_map_size;
+ rt_uint32_t charge_current_limit;
+
+ /* To be fill */
+ enum rt_power_supply_property gpio_charger_properties[3];
+};
+
+static rt_err_t set_charge_current_limit(struct gpio_charger *gpioc, int val)
+{
+ int i;
+ struct gpio_mapping mapping;
+ struct gpio_desc *gpios = gpioc->current_limit_gpios;
+
+ if (!gpioc->current_limit_map_size)
+ {
+ return -RT_EINVAL;
+ }
+
+ for (i = 0; i < gpioc->current_limit_map_size; i++)
+ {
+ if (gpioc->current_limit_map[i].limit_ua <= val)
+ {
+ break;
+ }
+ }
+ mapping = gpioc->current_limit_map[i];
+
+ for (i = 0; i < gpioc->current_limit_gpios_nr; i++)
+ {
+ rt_bool_t val = (mapping.gpiodata >> i) & 1;
+ struct gpio_desc *gpio = &gpios[gpioc->current_limit_gpios_nr - i - 1];
+
+ rt_pin_mode(gpio->pin, PIN_MODE_OUTPUT);
+ rt_pin_write(gpio->pin, val ? gpio->flags : !gpio->flags);
+ }
+
+ gpioc->charge_current_limit = mapping.limit_ua;
+
+ return RT_EOK;
+}
+
+static rt_err_t gpio_charger_get_property(struct rt_power_supply *psy,
+ enum rt_power_supply_property prop, union rt_power_supply_property_val *val)
+{
+ struct gpio_charger *gpioc = rt_container_of(psy, struct gpio_charger, parent);
+
+ switch (prop)
+ {
+ case RT_POWER_SUPPLY_PROP_ONLINE:
+ rt_pin_mode(gpioc->gpiod.pin, PIN_MODE_INPUT);
+ val->intval = rt_pin_read(gpioc->gpiod.pin) == gpioc->gpiod.flags;
+ break;
+
+ case RT_POWER_SUPPLY_PROP_STATUS:
+ rt_pin_mode(gpioc->charge_status.pin, PIN_MODE_INPUT);
+ if (rt_pin_read(gpioc->charge_status.pin) == gpioc->charge_status.flags)
+ {
+ val->intval = RT_POWER_SUPPLY_STATUS_CHARGING;
+ }
+ else
+ {
+ val->intval = RT_POWER_SUPPLY_STATUS_NOT_CHARGING;
+ }
+ break;
+
+ case RT_POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX:
+ val->intval = gpioc->charge_current_limit;
+ break;
+
+ default:
+ return -RT_ENOSYS;
+ }
+
+ return RT_EOK;
+}
+
+static rt_err_t gpio_charger_set_property(struct rt_power_supply *psy,
+ enum rt_power_supply_property prop, const union rt_power_supply_property_val *val)
+{
+ struct gpio_charger *gpioc = rt_container_of(psy, struct gpio_charger, parent);
+
+ switch (prop)
+ {
+ case RT_POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX:
+ return set_charge_current_limit(gpioc, val->intval);
+
+ default:
+ return -EINVAL;
+ }
+
+ return RT_EOK;
+}
+
+static const struct rt_power_supply_ops gpio_charger_ops =
+{
+ .get_property = gpio_charger_get_property,
+ .set_property = gpio_charger_set_property,
+};
+
+static void gpio_charger_isr(void *args)
+{
+ struct gpio_charger *gpioc = args;
+
+ rt_power_supply_changed(&gpioc->parent);
+}
+
+static rt_err_t init_charge_current_limit(struct rt_device *dev,
+ struct gpio_charger *gpioc)
+{
+ rt_ssize_t len;
+ rt_uint32_t cur_limit = RT_UINT32_MAX;
+
+ gpioc->current_limit_gpios_nr = rt_pin_get_named_pin_count(dev, "charge-current-limit");
+ if (gpioc->current_limit_gpios_nr <= 0)
+ {
+ return gpioc->current_limit_gpios_nr;
+ }
+
+ gpioc->current_limit_gpios = rt_malloc(gpioc->current_limit_gpios_nr *
+ sizeof(*gpioc->current_limit_gpios));
+ if (!gpioc->current_limit_gpios)
+ {
+ return RT_EOK;
+ }
+
+ len = rt_dm_dev_prop_count_of_u32(dev, "charge-current-limit-mapping");
+ if (len < 0)
+ {
+ return len;
+ }
+
+ if (len == 0 || len % 2)
+ {
+ return -RT_EINVAL;
+ }
+ gpioc->current_limit_map_size = len / 2;
+
+ gpioc->current_limit_map = rt_malloc(gpioc->current_limit_map_size *
+ sizeof(*gpioc->current_limit_map));
+ if (!gpioc->current_limit_map)
+ {
+ return -RT_ENOMEM;
+ }
+
+ len = rt_dm_dev_prop_read_u32_array_index(dev, "charge-current-limit-mapping",
+ 0, (int)len, (rt_uint32_t *)(void *)gpioc->current_limit_map);
+ if (len < 0)
+ {
+ return len;
+ }
+
+ for (int i = 0; i < gpioc->current_limit_map_size; ++i)
+ {
+ if (gpioc->current_limit_map[i].limit_ua > cur_limit)
+ {
+ return -RT_EINVAL;
+ }
+
+ cur_limit = gpioc->current_limit_map[i].limit_ua;
+ }
+
+ /* Default to smallest current limitation for safety reasons */
+ len = gpioc->current_limit_map_size - 1;
+ set_charge_current_limit(gpioc, gpioc->current_limit_map[len].limit_ua);
+
+ return RT_EOK;
+}
+
+static rt_err_t gpio_charger_probe(struct rt_platform_device *pdev)
+{
+ rt_err_t err;
+ int num_props = 0;
+ const char *chargetype;
+ struct gpio_charger *gpioc;
+ struct rt_device *dev = &pdev->parent;
+
+ gpioc = rt_calloc(1, sizeof(*gpioc));
+
+ if (!gpioc)
+ {
+ return -RT_ENOMEM;
+ }
+
+ gpioc->gpiod.pin = rt_pin_get_named_pin(dev, RT_NULL, 0, RT_NULL, &gpioc->gpiod.flags);
+
+ if (gpioc->gpiod.pin < 0 && gpioc->gpiod.pin != -RT_EEMPTY)
+ {
+ err = -RT_EINVAL;
+ goto _fail;
+ }
+
+ if (gpioc->gpiod.pin >= 0)
+ {
+ gpioc->gpio_charger_properties[num_props] = RT_POWER_SUPPLY_PROP_ONLINE;
+ ++num_props;
+ }
+
+ gpioc->charge_status.pin = rt_pin_get_named_pin(dev, "charge-status", 0,
+ RT_NULL, &gpioc->charge_status.flags);
+
+ if (gpioc->charge_status.pin < 0 && gpioc->charge_status.pin != -RT_EEMPTY)
+ {
+ err = -RT_EINVAL;
+ goto _fail;
+ }
+
+ if (gpioc->charge_status.pin >= 0)
+ {
+ gpioc->gpio_charger_properties[num_props] = RT_POWER_SUPPLY_PROP_STATUS;
+ ++num_props;
+ }
+
+ if ((err = init_charge_current_limit(dev, gpioc)))
+ {
+ goto _fail;
+ }
+
+ if (gpioc->current_limit_map)
+ {
+ gpioc->gpio_charger_properties[num_props] = RT_POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX;
+ ++num_props;
+ }
+
+ if (!rt_dm_dev_prop_read_string(dev, "charger-type", &chargetype))
+ {
+ if (!rt_strcmp("unknown", chargetype))
+ {
+ gpioc->parent.type = RT_POWER_SUPPLY_TYPE_UNKNOWN;
+ }
+ else if (!rt_strcmp("battery", chargetype))
+ {
+ gpioc->parent.type = RT_POWER_SUPPLY_TYPE_BATTERY;
+ }
+ else if (!rt_strcmp("ups", chargetype))
+ {
+ gpioc->parent.type = RT_POWER_SUPPLY_TYPE_UPS;
+ }
+ else if (!rt_strcmp("mains", chargetype))
+ {
+ gpioc->parent.type = RT_POWER_SUPPLY_TYPE_MAINS;
+ }
+ else if (!rt_strcmp("usb-sdp", chargetype))
+ {
+ gpioc->parent.type = RT_POWER_SUPPLY_TYPE_USB_SDP;
+ }
+ else if (!rt_strcmp("usb-dcp", chargetype))
+ {
+ gpioc->parent.type = RT_POWER_SUPPLY_TYPE_USB_DCP;
+ }
+ else if (!rt_strcmp("usb-cdp", chargetype))
+ {
+ gpioc->parent.type = RT_POWER_SUPPLY_TYPE_USB_CDP;
+ }
+ else if (!rt_strcmp("usb-aca", chargetype))
+ {
+ gpioc->parent.type = RT_POWER_SUPPLY_TYPE_USB_ACA;
+ }
+ else
+ {
+ gpioc->parent.type = RT_POWER_SUPPLY_TYPE_UNKNOWN;
+ }
+ }
+
+ gpioc->parent.dev = dev,
+ gpioc->parent.properties_nr = num_props,
+ gpioc->parent.properties = gpioc->gpio_charger_properties,
+ gpioc->parent.ops = &gpio_charger_ops;
+
+ if ((err = rt_power_supply_register(&gpioc->parent)))
+ {
+ goto _fail;
+ }
+
+ rt_pin_mode(gpioc->gpiod.pin, PIN_MODE_INPUT);
+ rt_pin_attach_irq(gpioc->gpiod.pin, PIN_IRQ_MODE_RISING, gpio_charger_isr, gpioc);
+ rt_pin_irq_enable(gpioc->gpiod.pin, RT_TRUE);
+
+ rt_pin_mode(gpioc->charge_status.pin, PIN_MODE_INPUT);
+ rt_pin_attach_irq(gpioc->charge_status.pin, PIN_IRQ_MODE_RISING, gpio_charger_isr, gpioc);
+ rt_pin_irq_enable(gpioc->charge_status.pin, RT_TRUE);
+
+ return RT_EOK;
+_fail:
+ if (gpioc->current_limit_map)
+ {
+ rt_free(gpioc->current_limit_map);
+ }
+ if (gpioc->current_limit_gpios)
+ {
+ rt_free(gpioc->current_limit_gpios);
+ }
+ rt_free(gpioc);
+
+ return err;
+}
+
+static const struct rt_ofw_node_id gpio_charger_ofw_ids[] =
+{
+ { .compatible = "gpio-charger" },
+ { /* sentinel */ }
+};
+
+static struct rt_platform_driver gpio_charger_driver =
+{
+ .name = "gpio-charger",
+ .ids = gpio_charger_ofw_ids,
+
+ .probe = gpio_charger_probe,
+};
+RT_PLATFORM_DRIVER_EXPORT(gpio_charger_driver);