Files
kunlun/plc/halmac/pm/mac_pm.c
2024-09-28 14:24:04 +08:00

634 lines
17 KiB
C
Executable File

/****************************************************************************
Copyright(c) 2019 by Aerospace C.Power (Chongqing) Microelectronics. ALL RIGHTS RESERVED.
This Information is proprietary to Aerospace C.Power (Chongqing) Microelectronics and MAY NOT
be copied by any method or incorporated into another program without
the express written consent of Aerospace C.Power. This Information or any portion
thereof remains the property of Aerospace C.Power. The Information contained herein
is believed to be accurate and Aerospace C.Power assumes no responsibility or
liability for its use in any way and conveys no license or title under
any patent or copyright and makes no representation or warranty that this
Information is free from patent or copyright infringement.
****************************************************************************/
#include "mac_pm.h"
#include "mac_msg.h"
#include "mac.h"
#include "mac_vdev.h"
#include "mac_pdev.h"
#include "mac_sched.h"
#include "mac_sched_hw.h"
#include "mac_tx_hw.h"
#include "iot_io.h"
#include "iot_clock.h"
#include "clk.h"
#include "cpu.h"
#include "ahb.h"
#include "phy_chn.h"
#include "phy_rf_chn.h"
#include "hw_phy_api.h"
#include "mac_init_api.h"
#include "mac_tx_power.h"
#include "mac_reset.h"
#include "iot_board.h"
#include "iot_oem_api.h"
#include "phy_rf_init.h"
#include "mac_rf_tx_power.h"
#include "os_utils_api.h"
#if PM_USE_DSR
extern mac_pdev_t* g_mac_pdev[MAX_PDEV_NUM];
extern mac_global_t* p_mac_glb;
#endif
typedef void (*mac_pm_switch_freq_cb_t)();
/* callback for mac pm switch freq function */
mac_pm_switch_freq_cb_t mac_pm_switch_freq_cb = NULL;
void register_mac_pm_switch_freq_cb(mac_pm_switch_freq_cb_t cb)
{
#if (PLC_PM_SWITCH_POLICY == 1)
mac_pm_switch_freq_cb = cb;
#endif
(void)cb;
}
void mac_pm_check_freq()
{
if (mac_pm_switch_freq_cb) {
mac_pm_switch_freq_cb();
}
}
static uint32_t mac_pm_get_band_sleep_intvl(uint32_t proto, \
uint32_t proto_bandid)
{
uint8_t sg_band_sleep_table_ms[MAX_SG_BAND_ID] = {
20, // band 0
25, // band 1
40, // band 2
60, // band 3
20, // band 4
0, // band 5
0, // band 6
0, // band 7
20, // band 8
20, // band 9
20, // band 10
20 // band 11
};
if (proto == PLC_PROTO_TYPE_SG) {
if (proto_bandid < MAX_SG_BAND_ID) {
return sg_band_sleep_table_ms[proto_bandid];
} else {
return sg_band_sleep_table_ms[MAX_SG_BAND_ID - 1];
}
}
else if(proto == PLC_PROTO_TYPE_SPG) {
if (proto_bandid < MAX_SG_BAND_ID) {
return sg_band_sleep_table_ms[proto_bandid];
} else {
return sg_band_sleep_table_ms[MAX_SG_BAND_ID - 1];
}
}
else if (proto == PLC_PROTO_TYPE_GP) {
return 3;
}
else {
IOT_ASSERT(0);
return 20;
}
}
static void deep_sleep_wake_up(uint32_t freq)
{
#if (PLC_PM_SWITCH_POLICY == 0)
/* change the clock to PLL */
system_cpu_wakeup(1, freq);
#endif
/* phy enter normal mode */
phy_idle_clr();
}
void mac_pm_init_wake_up()
{
/* requst mac pm stay awake */
req_stay_awake(MAC_PM_INIT_MODULE_ID, 1);
#if STATIC_POWER_SAVE
#if (PLC_PM_SWITCH_POLICY == 1)
/* change the clock to PLL */
system_cpu_wakeup(1, CPU_FREQ_150M);
#endif
deep_sleep_wake_up(CPU_FREQ_150M);
#endif
}
static uint32_t wake_up(uint8_t sleep_level)
{
mac_pdev_t *pdev = (mac_pdev_t *)g_mac_pdev[PLC_PDEV_ID];
if (sleep_level == DEEP_SLEEP_LEVEL_1) {
deep_sleep_wake_up(CPU_FREQ_75M);
pdev->mac_pm.cur_status = WAKE_UP;
} else if (sleep_level == DEEP_SLEEP_LEVEL_2) {
deep_sleep_wake_up(CPU_FREQ_150M);
pdev->mac_pm.cur_status = WAKE_UP;
}
return ERR_OK;
}
static uint32_t mac_pm_war_function(mac_pdev_t *pdev)
{
if (pdev == NULL) {
IOT_ASSERT(0);
}
#if WAR_FOR_LOW_POWER
/* once the phy txrxn state has been entered, this war is not enabled */
if (g_phy_ctxt.dep.raise_dcdccode_flag) {
pdev->mac_pm.pm_war_state = 0;
goto next;
}
#define MAC_PM_WAR_PERIOD_1200MS 2500
#define MAC_PM_WAR_PERIOD_19800MS 18500
uint32_t dlt = os_boot_time32() - pdev->mac_pm.mac_pm_anchor_ts;
if (pdev->mac_pm.pm_war_state) {
if (dlt >= MAC_PM_WAR_PERIOD_1200MS) {
pdev->mac_pm.pm_war_state = 0;
pdev->mac_pm.mac_pm_anchor_ts = os_boot_time32();
}
} else {
if (dlt >= MAC_PM_WAR_PERIOD_19800MS) {
pdev->mac_pm.pm_war_state = 1;
pdev->mac_pm.mac_pm_anchor_ts = os_boot_time32();
}
}
next:
#endif
return pdev->mac_pm.pm_war_state;
}
static uint32_t mac_pm_sleep_wakeup(mac_pdev_t *pdev)
{
if (NULL == pdev) {
IOT_ASSERT(0);
return ERR_INVAL;
}
/* make sure timer is on */
if (!pdev->mac_pm.is_start) {
if (pdev->mac_pm.sleep_timer) {
os_stop_timer(pdev->mac_pm.sleep_timer);
}
/* wake up */
if (pdev->mac_pm.cur_status == DEEP_SLEEP) {
wake_up(DEEP_SLEEP_LEVEL_2);
}
/* rf wake up */
phy_rf_set_ps_idle(0);
pdev->mac_pm.is_sleep_wakeup_valid = 0;
return ERR_INVAL;
}
pdev->mac_pm.wake_time_ms = mac_pm_get_band_sleep_intvl(
PHY_PROTO_TYPE_GET(),
phy_band_id_get());
pdev->mac_pm.sleep_time_ms = 2 * pdev->mac_pm.wake_time_ms;
mac_pm_war_function(pdev);
switch (pdev->mac_pm.cur_status) {
case WAKE_UP:
if (g_phy_cpu_share_ctxt.pm_status.bitmap == 0) {
sleep(DEEP_SLEEP_LEVEL_1);
if (!pdev->mac_pm.pm_war_state) {
/* rf wakeup */
phy_rf_set_ps_idle(0);
}
os_stop_timer(pdev->mac_pm.sleep_timer);
os_start_timer(pdev->mac_pm.sleep_timer,
pdev->mac_pm.sleep_time_ms);
} else if (pdev->mac_pm.fc_ok_pm &&
g_phy_cpu_share_ctxt.pm_status.fc_ok_found) {
phy_pkt_found_flag_clr();
os_stop_timer(pdev->mac_pm.sleep_timer);
os_start_timer(pdev->mac_pm.sleep_timer, pdev->mac_pm.wake_time_ms);
} else {
/* rf wake up */
phy_rf_set_ps_idle(0);
}
break;
case DEEP_SLEEP:
/* rf sleep */
phy_rf_set_ps_idle(1);
wake_up(DEEP_SLEEP_LEVEL_1);
os_stop_timer(pdev->mac_pm.sleep_timer);
os_start_timer(pdev->mac_pm.sleep_timer, pdev->mac_pm.wake_time_ms);
break;
default:
IOT_ASSERT(0);
}
pdev->mac_pm.is_sleep_wakeup_valid = 0;
return ERR_OK;
}
void mac_pm_rx_power_off(uint32_t arg, uint32_t data)
{
iot_printf("%s\n", __FUNCTION__);
mac_msg_t *msg = mac_alloc_msg();
(void)data;
if (msg == NULL) {
IOT_ASSERT(0);
goto out;
}
msg->type = MAC_MSG_TYPE_PM;
msg->id = MAC_MSG_ID_PM_OFF;
msg->data1 = arg;
mac_queue_msg(msg, MAC_MSG_QUEUE_HP);
out:
return;
}
uint32_t mac_pm_rx_power_off_internal(uint8_t pdev_id, uint8_t vdev_id)
{
mac_vdev_t * vdev = get_vdev_ptr(pdev_id, vdev_id);
mac_pdev_t *pdev_ptr = (mac_pdev_t *)g_mac_pdev[pdev_id];
if (vdev == NULL) {
return ERR_INVAL;
}
iot_printf("%s, pdevid=%d, dbgvdevid:%d, vdevid = %d\n",
__FUNCTION__, pdev_id, pdev_ptr->dbg_pkt_vdev_id, vdev_id);
if (vdev->is_up == 0) {
return ERR_OK;
}
if (pdev_ptr->dbg_pkt_vdev_id == vdev->vdev_id) {
return ERR_OK;
}
pdev_ptr->mac_pm.mac_pm_flag = 1; //flag for power off
if (pdev_ptr->mac_pm.pwroff_timer &&
pdev_ptr->mac_pm.power_off_high_power) {
os_start_timer(pdev_ptr->mac_pm.pwroff_timer, MAC_HP_FOR_PWROFF);
}
/* set high pwr for pwroff */
mac_high_power_req_ena(vdev, false);
if (pdev_ptr->mac_pm.power_off_high_power) {
uint8_t hplc_power = PHY_FULL_PWR_DBUV;
int8_t rf_power = RF_TX_PWR_INVALID;
phy_rf_get_power(NULL, NULL, NULL, &rf_power);
mac_set_tx_power_cap(vdev, &hplc_power, &rf_power, 1);
} else {
uint8_t hplc_power = 0;
int8_t rf_power = RF_TX_PWR_INVALID;
mac_set_tx_power_cap(vdev, &hplc_power, &rf_power, 1);
}
/* CCO does not process power off handle */
if (mac_vdev_cfg_get_node_role(vdev) == PLC_DEV_ROLE_CCO) {
return ERR_OK;
}
#if DEBUG_HWQ_SHCED_HANG
pdev_ptr->dbg_status = MAC_TX_HANG_STATUS_3;
#endif
/* clear tx packet in hw queue */
mac_pre_phy_reinit();
#if DEBUG_HWQ_SHCED_HANG
pdev_ptr->dbg_status = MACC_TX_HANG_STATUS_MAX;
#endif
/* restart and enable tx */
mac_post_phy_reinit(1);
return 0;
}
uint32_t mac_pm_power_recover_internal(uint8_t pdev_id, uint8_t vdev_id)
{
mac_vdev_t * vdev = get_vdev_ptr(pdev_id, vdev_id);
mac_pdev_t *pdev_ptr = (mac_pdev_t *)g_mac_pdev[pdev_id];
if (vdev == NULL) {
return ERR_INVAL;
}
iot_printf("%s, pdevid=%d, dbgvdevid:%d, vdevid = %d\n",
__FUNCTION__, pdev_id, pdev_ptr->dbg_pkt_vdev_id, vdev_id);
if (vdev->is_up == 0) {
return ERR_OK;
}
if (pdev_ptr->dbg_pkt_vdev_id == vdev->vdev_id) {
return ERR_OK;
}
pdev_ptr->mac_pm.mac_pm_flag = 0; //flag for power recover
if (pdev_ptr->mac_pm.pwroff_timer) {
os_stop_timer(pdev_ptr->mac_pm.pwroff_timer);
}
#if DEBUG_HWQ_SHCED_HANG
pdev_ptr->dbg_status = MAC_TX_HANG_STATUS_5;
#endif
/* clear tx packet in hw queue */
mac_pre_phy_reinit();
#if DEBUG_HWQ_SHCED_HANG
pdev_ptr->dbg_status = MACC_TX_HANG_STATUS_MAX;
#endif
mac_fix_power_apply(vdev);
/* restart and disable tx */
mac_post_phy_reinit(0);
return ERR_OK;
}
void sleep_cfg(timer_id_t timer_id, void * arg)
{
#if PM_USE_DSR
(void)timer_id;
(void)arg;
os_set_task_event_with_v(p_mac_glb->task_h, \
1 << MAC_DSR_POWER_PM_ID);
#else
mac_pdev_t *pdev = (mac_pdev_t*)arg;
(void) timer_id;
if (!pdev->mac_pm.is_sleep_wakeup_valid) {
/* timer context */
mac_msg_t *msg = mac_alloc_msg();
if (msg == NULL) {
IOT_ASSERT(0);
return;
}
pdev->mac_pm.is_sleep_wakeup_valid = 1;
msg->type = MAC_MSG_TYPE_TIMER;
msg->id = MAC_MSG_ID_SYSTEM_SLEEP;
msg->data1 = 0;
msg->data2 = arg;
mac_queue_msg(msg, MAC_MSG_QUEUE_MP);
}
#endif
return;
}
void mac_pm_switch_freq()
{
#if (PLC_PM_SWITCH_POLICY == 1)
mac_pdev_t *pdev = get_pdev_ptr(PLC_PDEV_ID);
if (pdev->mac_pm.is_check_freq && cpu_get_clock() != CPU_FREQ_150M) {
pdev->mac_pm.is_check_freq = 0;
/* change the clock to 150M */
system_cpu_wakeup(1, CPU_FREQ_150M);
}
#endif
}
uint32_t mac_pm_init(void *pdev_in)
{
mac_pdev_t *pdev = (mac_pdev_t*) pdev_in;
#if STATIC_POWER_SAVE
pdev->mac_pm.wake_time_ms = mac_pm_get_band_sleep_intvl(
PHY_PROTO_TYPE_GET(),
phy_proto_single_band_id_get());
pdev->mac_pm.sleep_time_ms = 2 * pdev->mac_pm.wake_time_ms;
pdev->mac_pm.cur_status = WAKE_UP; //cur status wake up
pdev->mac_pm.is_sleep_wakeup_valid = 0;
pdev->mac_pm.is_start = 0;
/* init awake reason, except for reason MAC_PM_CERT_TEST_MODULE_ID */
phy_pkt_found_flag_clr();
g_phy_cpu_share_ctxt.pm_status.band_select_ok = 0;
req_stay_awake(MAC_PM_INIT_MODULE_ID, false);
req_stay_awake(MAC_PM_SCAN_MODULE_ID, false);
if (pdev->mac_pm.sleep_timer == 0) {
pdev->mac_pm.sleep_timer = os_create_timer(PLC_MAC_STATUSE_MID, false,
sleep_cfg, pdev);
/* record mac pm start ts */
pdev->mac_pm.mac_pm_anchor_ts = os_boot_time32();
pdev->mac_pm.pm_war_state = 0;
}
/* enable brg async */
ahb_rf_brg_async();
register_mac_pm_switch_freq_cb(mac_pm_switch_freq);
#endif
if (pdev->mac_pm.pwroff_timer == 0) {
pdev->mac_pm.pwroff_timer = os_create_timer(PLC_MAC_STATUSE_MID, \
false, low_pwr_for_powroff_cfg, pdev);
}
/* power off high power */
uint32_t hw_ver = iot_board_hw_version_hex();
if (HW_VERSION_GS_NO_IPD_STA != hw_ver &&
HW_VERSION_GS_NO_IPD_STA_3P != hw_ver) {
pdev->mac_pm.power_off_high_power = 1;
} else {
pdev->mac_pm.power_off_high_power = 0;
}
return ERR_OK;
}
uint32_t mac_pm_start(void *pdev_in)
{
IOT_ASSERT(pdev_in);
mac_pdev_t *pdev = (mac_pdev_t*) pdev_in;
if (pdev->mac_pm.sleep_timer) {
phy_pkt_found_flag_clr();
os_start_timer(pdev->mac_pm.sleep_timer, pdev->mac_pm.sleep_time_ms);
pdev->mac_pm.is_start = 1;
pdev->mac_pm.is_check_freq = 0;
#if (PLC_PM_SWITCH_POLICY == 1)
system_cpu_sleep(1);
#endif
} else {
return ERR_FAIL;
}
return ERR_OK;
}
uint32_t mac_pm_stop(void *pdev_in)
{
IOT_ASSERT(pdev_in);
mac_pdev_t *pdev = (mac_pdev_t*) pdev_in;
if (pdev->mac_pm.sleep_timer) {
if (pdev->mac_pm.cur_status == DEEP_SLEEP) {
wake_up(DEEP_SLEEP_LEVEL_2);
}
/* rf wake up */
phy_rf_set_ps_idle(0);
os_stop_timer(pdev->mac_pm.sleep_timer);
pdev->mac_pm.is_start = 0;
pdev->mac_pm.is_check_freq = 1;
} else {
return ERR_FAIL;
}
return ERR_OK;
}
void deep_sleep()
{
/* phy and analog enter IDLE */
phy_idle_set();
#if (IOT_SMART_GRID_ENABLE && HW_PLATFORM >= HW_PLATFORM_FPGA)
if (cpu_get_clock() != CPU_FREQ_25M) {
/* change the clock to 25M */
system_cpu_sleep(1);
}
#endif
}
uint32_t sleep(uint8_t sleep_level)
{
mac_pdev_t *pdev = (mac_pdev_t *)g_mac_pdev[PLC_PDEV_ID];
if (sleep_level == DEEP_SLEEP_LEVEL_1) {
deep_sleep();
pdev->mac_pm.cur_status = DEEP_SLEEP;
}
return ERR_OK;
}
uint32_t req_stay_awake(uint32_t module_bit, uint32_t req_awake)
{
if (req_awake) {
g_phy_cpu_share_ctxt.pm_status.bitmap |= module_bit;
} else {
g_phy_cpu_share_ctxt.pm_status.bitmap &= ~module_bit;
}
return 0;
}
#if PM_USE_DSR
void mac_pm_dsr_sleep_cfg(void)
{
mac_pdev_t *pdev = (mac_pdev_t *)g_mac_pdev[PLC_PDEV_ID];
/* because the dsr event handle maybe delay for some long time task,
* for example vdev_stop, the pm timer handler may be handled after
* timer stopped, to avoid this, we introduced this flag.
*/
if (pdev->mac_pm.is_start == 1) {
mac_pm_sleep_wakeup(g_mac_pdev[PLC_PDEV_ID]);
}
}
#else
uint32_t sleep_cfg_internal(void *arg)
{
return mac_pm_sleep_wakeup((mac_pdev_t*)arg);
}
#endif
void low_pwr_for_powroff_cfg(timer_id_t timer_id, void * arg)
{
iot_printf("%s\n", __FUNCTION__);
mac_msg_t *msg = mac_alloc_msg();
if (msg == NULL) {
IOT_ASSERT(0);
goto out;
}
msg->type = MAC_MSG_TYPE_PM;
msg->id = MAC_MSG_ID_PWR_CHANGE;
msg->data1 = timer_id;
msg->data2 = arg;
mac_queue_msg(msg, MAC_MSG_QUEUE_HP);
out:
return;
}
void low_pwr_for_powroff_cfg_internal(timer_id_t timer_id, void * arg)
{
(void)timer_id;
mac_pdev_t *pdev_ptr = (mac_pdev_t*)arg;
mac_vdev_t * vdev = pdev_ptr->vdev[PLC_DEFAULT_VDEV];
(void)vdev;
if (g_phy_cpu_share_ctxt.tx_pwr_ctl_ena == 0 ||
pdev_ptr->mac_pm.mac_pm_flag) {
uint8_t hplc_power = 0;
int8_t rf_power = RF_TX_PWR_INVALID;
mac_set_tx_power_cap(vdev, &hplc_power, &rf_power, 1);
}
}
uint32_t mac_pm_switch_by_fc_ok(uint8_t *mac_addr)
{
IOT_ASSERT(mac_addr);
uint32_t ret = 0;
const uint8_t addr1[6]={0x11,0x00,0x00,0x00,0x00,0x01};
const uint8_t addr2[6]={0x11,0x00,0x00,0x00,0x00,0x02};
const uint8_t addr3[6]={0x11,0x00,0x00,0x00,0x00,0x03};
const uint8_t addr4[6]={0x11,0x00,0x00,0x00,0x00,0x04};
const uint8_t addr5[6]={0x11,0x00,0x00,0x00,0x00,0x05};
const uint8_t addr6[6]={0x11,0x00,0x00,0x00,0x00,0x06};
const uint8_t addr7[6]={0x11,0x00,0x00,0x00,0x00,0x07};
const uint8_t addr8[6]={0x11,0x00,0x00,0x00,0x00,0x08};
iot_printf("---%s:"
"0x%02x:0x%02x:0x%02x:0x%02x:0x%02x:0x%02x\n",
__FUNCTION__,
mac_addr[0],
mac_addr[1],
mac_addr[2],
mac_addr[3],
mac_addr[4],
mac_addr[5]
);
if (((iot_mac_addr_cmp(mac_addr, addr1) ||
iot_mac_addr_cmp(mac_addr, addr2) ||
iot_mac_addr_cmp(mac_addr, addr3) ||
iot_mac_addr_cmp(mac_addr, addr4) ||
iot_mac_addr_cmp(mac_addr, addr5) ||
iot_mac_addr_cmp(mac_addr, addr6) ||
iot_mac_addr_cmp(mac_addr, addr7) ||
iot_mac_addr_cmp(mac_addr, addr8)) &&
(USER_TYPE_SOUTHEN_POWER_GRID_HAINAN ==
iot_oem_get_user_type())) || RX_FC_ON_POWER_SAVE) {
iot_printf("enter sleep mode always\n");
ret = 1;
}
return ret;
}