sdk-hwV1.3/lichee/melis-v3.0/source/ekernel/components/aw/standby/standby_e907.c

212 lines
4.3 KiB
C

#include <hal_osal.h>
#include <hal_log.h>
#include <hal_msgbox.h>
#include <errno.h>
#include <hal_uart.h>
#include <standby/standby.h>
#include "pm_internal.h"
// #define DEBUG
#define MSGBOX_RISCV 1
#define MSGBOX_ARM 0
/* msgbox config */
#define MSGBOX_REMOTE MSGBOX_ARM
#define MSGBOX_R_CH CONFIG_STANDBY_MSGBOX_CHANNEL
#define MSGBOX_W_CH CONFIG_STANDBY_MSGBOX_CHANNEL
#define IRQ_MSGBOX CONFIG_STANDBY_MSGBOX_IRQ
#define PM_POWER_SUSPEND 0xf3f30101
#define PM_POWER_ACK 0xf3f30102
#define PM_POWER_DEAD 0xf3f30103
#define PM_POWER_RESUME 0xf3f30204
#ifdef DEBUG
#define debug(fmt, args...) hal_log_debug(fmt, ##args)
#else
#define debug(fmt, ...)
#endif
extern void irq_suspend(void);
extern void irq_resume(void);
extern void clic_resume(void);
extern void clic_suspend(void);
extern void cpu_suspend(void);
extern long g_sleep_flag;
struct sunxi_pm {
struct msg_endpoint ept;
void *thread;
hal_queue_t recv_queue;
};
static void msgbox_recv_callback(uint32_t data, void *priv)
{
int ret = 0;
struct sunxi_pm *inst = priv;
#ifdef DEBUG
int len;
len = hal_queue_len(inst->recv_queue);
debug("queue len:%d -> %d:data:%x\n", len, len + 1, data);
#endif
ret = hal_queue_send(inst->recv_queue, &data);
if (ret != 0)
hal_log_warn("fail to send data to queue\n");
}
static void sunxi_standby_send_ack(struct sunxi_pm *inst)
{
uint32_t data = PM_POWER_ACK;
hal_msgbox_channel_send(&inst->ept, (uint8_t *)&data, sizeof(uint32_t));
}
static void sunxi_standby_send_will(struct sunxi_pm *inst)
{
uint32_t data = PM_POWER_DEAD;
debug("send will");
hal_msgbox_channel_send(&inst->ept, (uint8_t *)&data, sizeof(uint32_t));
}
static void cpu_to_low_power(struct sunxi_pm *inst)
{
clic_suspend();
cpu_suspend();
if (g_sleep_flag) {
debug("enter standby");
hal_dcache_clean_all();
sunxi_standby_send_will(inst);
__asm__ __volatile__("wfi");
}
debug("leave standby");
clic_resume();
}
static void standby_suspend_prepare(struct sunxi_pm *inst)
{
/* do something before suspend */
dev_pm_notify_call(DEV_NOTIFY_SUSPEND);
return;
}
static void standby_resume_prepare(struct sunxi_pm *inst)
{
/* do something before resume */
dev_pm_notify_call(DEV_NOTIFY_RESUME);
return;
}
static void standby_enter(struct sunxi_pm *inst)
{
hal_interrupt_disable();
/* disable all interrupt */
irq_suspend();
/* only enable msgbox interrupt */
hal_enable_irq(IRQ_MSGBOX);
cpu_to_low_power(inst);
irq_resume();
standby_resume_prepare(inst);
hal_interrupt_enable();
}
static void standby_thread(void *arg)
{
int ret = 0;
#ifdef DEBUG
int len;
#endif
uint32_t data = 0;
struct sunxi_pm *inst = arg;
while (1) {
data = 0;
ret = hal_queue_recv(inst->recv_queue, &data, -1);
if (ret != 0) {
hal_log_warn("fail to read data from queue\r\n");
continue;
}
#ifdef DEBUG
len = hal_queue_len(inst->recv_queue);
debug("queue len:%d -> %d:data:%x\n", len + 1, len, data);
#endif
switch (data) {
case PM_POWER_SUSPEND:
debug("send suspend ack");
sunxi_standby_send_ack(inst);
standby_suspend_prepare(inst);
standby_enter(inst);
debug("standby end");
break;
case PM_POWER_RESUME:
debug("send resume ack");
sunxi_standby_send_ack(inst);
break;
default:
hal_log_warn("Undown data:0x%08x\r\n", data);
}
}
}
int pm_standby_init(void)
{
int ret = 0;
struct sunxi_pm *inst = NULL;
inst = hal_malloc(sizeof(*inst));
if (!inst) {
hal_log_err("fail to alloc mem\r\n");
ret = -ENOMEM;
goto out;
}
inst->ept.rec = msgbox_recv_callback;
inst->ept.private = inst;
ret = hal_msgbox_alloc_channel(&inst->ept, MSGBOX_REMOTE, MSGBOX_R_CH, MSGBOX_W_CH);
if (ret) {
hal_log_err("fail to alloc msgbox channel(%d %d %d)\n", MSGBOX_REMOTE,
MSGBOX_R_CH, MSGBOX_W_CH);
ret = -EINVAL;
goto free_inst;
}
inst->recv_queue = hal_queue_create("standby-queue", sizeof(uint32_t), 4);
if (!inst->recv_queue) {
hal_log_err("fail to create queue\r\n");
ret = -ENOMEM;
goto free_channel;
}
inst->thread = kthread_create(standby_thread, inst, "standby", 4 * 1024, 1);
if (!inst->thread) {
hal_log_err("fail to create standby thread\r\n");
ret = -ENOMEM;
goto free_queue;
}
kthread_start(inst->thread);
return 0;
free_queue:
hal_queue_delete(inst->recv_queue);
free_channel:
hal_msgbox_free_channel(&inst->ept);
free_inst:
hal_free(inst);
out:
return ret;
}