Просмотр исходного кода

[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 <2991707448@qq.com>
GuEe-GUI 4 недель назад
Родитель
Сommit
df990e91d3

+ 2 - 0
components/drivers/power/Kconfig

@@ -0,0 +1,2 @@
+rsource "reset/Kconfig"
+rsource "supply/Kconfig"

+ 11 - 0
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')

+ 32 - 0
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

+ 18 - 0
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')

+ 100 - 0
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);

+ 100 - 0
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);

+ 105 - 0
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;
+}

+ 27 - 0
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__ */

+ 104 - 0
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);

+ 115 - 0
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);

+ 104 - 0
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);

+ 15 - 0
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

+ 6 - 0
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')

+ 379 - 0
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);

+ 346 - 0
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);