[dm][power] add new drivers for power framework
Some checks failed
ToolsCI / Tools (push) Has been cancelled
RT-Thread BSP Static Build Check / 🔍 Summary of Git Diff Changes (push) Has been cancelled
doc_doxygen / doxygen_doc generate (push) Has been cancelled
pkgs_test / change (push) Has been cancelled
utest_auto_run / A9 :components/dfs.cfg (push) Has been cancelled
utest_auto_run / A9 :components/lwip.cfg (push) Has been cancelled
utest_auto_run / A9 :components/netdev.cfg (push) Has been cancelled
utest_auto_run / A9 :components/sal.cfg (push) Has been cancelled
utest_auto_run / A9 :cpp11/cpp11.cfg (push) Has been cancelled
utest_auto_run / AARCH64-rtsmart :default.cfg (push) Has been cancelled
utest_auto_run / A9-rtsmart :default.cfg (push) Has been cancelled
utest_auto_run / RISCV-rtsmart :default.cfg (push) Has been cancelled
utest_auto_run / XUANTIE-rtsmart :default.cfg (push) Has been cancelled
utest_auto_run / AARCH64 :default.cfg (push) Has been cancelled
utest_auto_run / AARCH64-smp :default.cfg (push) Has been cancelled
utest_auto_run / A9 :default.cfg (push) Has been cancelled
utest_auto_run / A9-smp :default.cfg (push) Has been cancelled
utest_auto_run / RISCV :default.cfg (push) Has been cancelled
utest_auto_run / RISCV-smp :default.cfg (push) Has been cancelled
utest_auto_run / A9 :kernel/atomic_c11.cfg (push) Has been cancelled
utest_auto_run / RISCV :kernel/atomic_c11.cfg (push) Has been cancelled
utest_auto_run / A9 :kernel/ipc.cfg (push) Has been cancelled
utest_auto_run / A9 :kernel/kernel_basic.cfg (push) Has been cancelled
utest_auto_run / A9 :kernel/mem.cfg (push) Has been cancelled
RT-Thread BSP Static Build Check / ${{ matrix.legs.RTT_BSP }} (push) Has been cancelled
RT-Thread BSP Static Build Check / collect-artifacts (push) Has been cancelled
doc_doxygen / deploy (push) Has been cancelled
Weekly CI Scheduler / Trigger and Monitor CIs (push) Has been cancelled
Weekly CI Scheduler / Create Discussion Report (push) Has been cancelled

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>
This commit is contained in:
GuEe-GUI
2025-12-15 12:22:24 +08:00
committed by R b b666
parent b4d332706e
commit df990e91d3
15 changed files with 1464 additions and 0 deletions

View File

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

View File

@@ -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')

View File

@@ -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

View File

@@ -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')

View File

@@ -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);

View File

@@ -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);

View File

@@ -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;
}

View File

@@ -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__ */

View File

@@ -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);

View File

@@ -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);

View File

@@ -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);

View File

@@ -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

View File

@@ -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')

View File

@@ -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);

View File

@@ -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);