Files
kunlun/plc/halmac/hw/sched/mac_sched_hw.c
2024-09-28 14:24:04 +08:00

519 lines
15 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.
****************************************************************************/
/* os shim includes */
#include "os_types.h"
/* mac module internal includes */
#include "mac_vdev.h"
#include "mac_pdev.h"
#include "command_list.h"
#include "hw_reg_api.h"
#include "mac_sys_reg.h"
#include "mac_hwq_reg.h"
/* public api includes */
#include "plc_fr.h"
#include "tx_mpdu_start.h"
#include "mac_txq_hw.h"
#include "mac_status.h"
#include "os_utils.h"
#include "mac_reset.h"
#include "os_task.h"
#if MAC_SCHED_HW_DEBUG || ENA_WAR_911
#include "iot_io.h"
#endif
#include "multi_nid_sync.h"
#include "phy_reg.h"
#if HW_PLATFORM >= HW_PLATFORM_FPGA
#if ENA_WAR_440
#include "phy_bb.h"
#endif
void mac_sched_enable_hw_ntb_sync(mac_vdev_t *vdev, uint8_t enable)
{
uint32_t value;
/* disable irq */
os_disable_irq();
value = RGF_MAC_READ_REG(CFG_NTB_SYNC_0_ADDR);
if (enable) {
REG_FIELD_SET(CFG_HW_NTB_SYNC_EN, value, 1);
REG_FIELD_SET(CFG_NTB_SW_SYNC_CLR, value, 1);
} else {
REG_FIELD_SET(CFG_HW_NTB_SYNC_EN, value, 0);
REG_FIELD_SET(CFG_NTB_HW_SYNC_CLR, value, 1);
}
RGF_MAC_WRITE_REG(CFG_NTB_SYNC_0_ADDR, value);
/* enable irq */
os_enable_irq();
}
void mac_record_sync_ntb_overflow(uint64_t st_ntb, uint32_t result)
{
/* bitmap 0 1 3 4 */
#define SYNC_OVERFLOW_NTB_WAY_1 0x1B
/* bitmap 0 1 3 5 */
#define SYNC_OVERFLOW_NTB_WAY_2 0x2B
/* bitmap 0 2 3 4 */
#define SYNC_OVERFLOW_NTB_WAY_3 0x1D
/* bitmap 0 2 3 5 */
#define SYNC_OVERFLOW_NTB_WAY_4 0x2D
/* bitmap 6 */
#define SYNC_OVERFLOW_NTB_WAY_5 0x40
mac_pdev_t *pdev = get_pdev_ptr(PLC_PDEV_ID);
mac_sync_ntb_record_t *sync_ntb_record = &pdev->mac_status.sync_ntb_record;
uint64_t cur_ntb = mac_sched_get_ntb64(NULL);
switch (result) {
case SYNC_OVERFLOW_NTB_WAY_1 :
sync_ntb_record->sync_ntb_ovf_way1_cnt++;
if (iot_uint64_higher32(cur_ntb) - iot_uint64_higher32(st_ntb) == 1) {
sync_ntb_record->way1_success_cnt++;
}
break;
case SYNC_OVERFLOW_NTB_WAY_2 :
sync_ntb_record->sync_ntb_ovf_way2_cnt++;
if (iot_uint64_higher32(cur_ntb) - iot_uint64_higher32(st_ntb) == 1) {
sync_ntb_record->way2_success_cnt++;
}
break;
case SYNC_OVERFLOW_NTB_WAY_3 :
sync_ntb_record->sync_ntb_ovf_way3_cnt++;
if (iot_uint64_higher32(cur_ntb) - iot_uint64_higher32(st_ntb) == 1) {
sync_ntb_record->way3_success_cnt++;
}
break;
case SYNC_OVERFLOW_NTB_WAY_4 :
sync_ntb_record->sync_ntb_ovf_way4_cnt++;
if (iot_uint64_higher32(cur_ntb) - iot_uint64_higher32(st_ntb) == 1) {
sync_ntb_record->way4_success_cnt++;
}
break;
case SYNC_OVERFLOW_NTB_WAY_5 :
sync_ntb_record->sync_ntb_ovf_way5_cnt++;
break;
default:
sync_ntb_record->sync_ntb_ovf_way6_cnt++;
break;
}
}
void mac_sched_sync_ntb(mac_vdev_t *vdev, uint16_t golden_gap,
int32_t ntb_delta)
{
uint32_t value;
(void)vdev;
/* disable irq */
os_disable_irq();
value = RGF_MAC_READ_REG(CFG_NTB_SYNC_0_ADDR);
value |= CFG_NTB_DELTA_VAL_SIGN_MASK | CFG_MODIFY_NTB_EN_MASK;
if (golden_gap) {
/* set golden gap only when it's non-zero, this value shouldn't be
* changed once set.
*/
value &= ~CFG_NTB_GOLDEN_GAP_MASK;
value |= (((uint32_t)golden_gap) << CFG_NTB_GOLDEN_GAP_OFFSET)
& CFG_NTB_GOLDEN_GAP_MASK;
}
if (!(value & CFG_HW_NTB_SYNC_EN_MASK)) {
/* make sure HW don't sync NTB blindly */
IOT_ASSERT(RGF_MAC_READ_REG(CFG_RO_NTB_HW_SYNC_VAL_ADDR) == 0);
}
uint32_t u_ntb_delta = 0;
#if ENA_WAR_911
uint32_t result = 0;
/* war for hw bug, low bit overflow high bit not + 1 */
uint32_t ntb_delta_a = 0;
uint32_t ntb_delta_b = 0;
uint64_t cur_ntb = mac_sched_get_ntb64(NULL);
if (ntb_delta >= 0) {
/* positive */
uint64_t temp_ntb = (uint64_t)iot_uint64_lower32(cur_ntb) + ntb_delta;
/* if overflow */
if (iot_uint64_higher32(temp_ntb) >= 1) {
result |= 1 << 0;
/* set delta part a */
ntb_delta_a = MAX_UINT32_NTB - iot_uint64_lower32(cur_ntb);
if (ntb_delta_a > (RESERVE_NTB + (RESERVE_NTB >> 1))) {
result |= 1 << 1;
ntb_delta_a = ntb_delta_a - RESERVE_NTB;
/* set part a to register */
RGF_MAC_WRITE_REG(CFG_NTB_SYNC_1_ADDR, ntb_delta_a);
RGF_MAC_WRITE_REG(CFG_NTB_SYNC_0_ADDR, value);
} else {
result |= 1 << 2;
ntb_delta_a = 0;
}
IOT_ASSERT(ntb_delta >= ntb_delta_a);
/* set delta part b */
ntb_delta_b = ntb_delta - ntb_delta_a;
/* delay to wait hight bit +1 */
temp_ntb = mac_sched_get_ntb64(NULL);
/* check ntb is normal */
if (iot_uint64_higher32(temp_ntb) >= iot_uint64_higher32(cur_ntb)) {
result |= 1 << 3;
/* wait overflow happen */
uint32_t time_span = 0;
uint32_t cur_ntb_temp = 0;
do {
cur_ntb_temp = mac_sched_get_ntb(NULL);
time_span = cur_ntb_temp - iot_uint64_lower32(temp_ntb);
} while (cur_ntb_temp > (MAX_UINT32_NTB >> 1) &&
time_span < DELAY_MAX_NTB);
/* check which condition jumps out of the while */
if (cur_ntb_temp < (MAX_UINT32_NTB >> 1)) {
result |= 1 << 4;
/* set delta next part */
u_ntb_delta = ntb_delta_b;
} else {
result |= 1 << 5;
/* timeout and give up this sync part b */
u_ntb_delta = 0;
iot_printf("%s wait overflow timeout\n", __FUNCTION__);
}
} else {
iot_printf("%s sync ntb error\n", __FUNCTION__);
IOT_ASSERT(0);
}
} else {
u_ntb_delta = ntb_delta;
}
} else {
/* negative */
if ((iot_uint64_lower32(cur_ntb) < (-ntb_delta)) &&
(vdev_get_tei(vdev) != PLC_TEI_INVAL)) {
result |= 1 << 6;
u_ntb_delta = iot_uint64_lower32(cur_ntb);
} else {
u_ntb_delta = MAX_UINT32_NTB + ntb_delta;
}
}
/* launch ntb delta first as enable NTB modify bit will cause HW
* launch the delta value right away.
*/
RGF_MAC_WRITE_REG(CFG_NTB_SYNC_1_ADDR, u_ntb_delta);
RGF_MAC_WRITE_REG(CFG_NTB_SYNC_0_ADDR, value);
/* enable irq */
os_enable_irq();
if (result) {
mac_record_sync_ntb_overflow(cur_ntb, result);
}
#else
if (ntb_delta < 0) {
u_ntb_delta = MAX_UINT32_NTB + ntb_delta;
} else {
u_ntb_delta = ntb_delta;
}
/* launch ntb delta first as enable NTB modify bit will cause HW
* launch the delta value right away.
*/
RGF_MAC_WRITE_REG(CFG_NTB_SYNC_1_ADDR, u_ntb_delta);
RGF_MAC_WRITE_REG(CFG_NTB_SYNC_0_ADDR, value);
/* enable irq */
os_enable_irq();
#endif
#if MAC_SCHED_HW_DEBUG
iot_printf("MAC_SCHED_HW ---%s--- CFG_NTB_SYNC_0_ADDR %x\n", __FUNCTION__,
value);
iot_printf("MAC_SCHED_HW ---%s--- CFG_NTB_SYNC_1_ADDR %d\n", __FUNCTION__,
ntb_delta);
#endif
}
uint32_t IRAM_ATTR mac_sched_get_ntb(mac_vdev_t *vdev)
{
(void)vdev;
return RGF_MAC_READ_REG(CFG_RD_NTB_ADDR);
}
uint32_t IRAM_ATTR mac_sched_get_lts()
{
return RGF_MAC_READ_REG(CFG_RD_LOCAL_TMR_ADDR);
}
uint32_t mac_sched_get_neighbor_ntb(mac_vdev_t *vdev, nid_t nid)
{
#if (HW_PLATFORM >= HW_PLATFORM_FPGA)
if (mac_multi_sync_get_ena(vdev->ref_pdev_id)) {
uint32_t ntb = RGF_MAC_READ_REG(CFG_RD_NTB_ADDR);
if (ERR_OK == mac_myntb_to_other_ntb(vdev->ref_pdev_id, nid, &ntb, 1)) {
return ntb;
} else {
return 0;
}
} else {
return 0;
}
#else
(void)vdev;
(void)nid;
return 0;
#endif
}
uint64_t IRAM_ATTR mac_sched_get_ntb64(mac_vdev_t *vdev)
{
uint32_t ntb;
uint64_t ntb64_a, ntb64_b;
(void)vdev;
ntb64_a = RGF_MAC_READ_REG(CFG_RO_NTB_TMR_WRAP_ADDR);
ntb = RGF_MAC_READ_REG(CFG_RD_NTB_ADDR);
ntb64_b = RGF_MAC_READ_REG(CFG_RO_NTB_TMR_WRAP_ADDR);
if (ntb64_b != ntb64_a) {
/* wrap round happened */
ntb = RGF_MAC_READ_REG(CFG_RD_NTB_ADDR);
}
ntb64_b = (ntb64_b << 32) | ntb;
return ntb64_b;
}
void mac_sched_set_bp_ahead_alert(mac_vdev_t *vdev, uint16_t ahead)
{
uint32_t value;
(void)vdev;
value = (((uint32_t)ahead) << CFG_BCN_ALERT_AHEAD_OFFSET)
& CFG_BCN_ALERT_AHEAD_MASK;
RGF_MAC_WRITE_REG(CFG_BEACON_ADDR, value);
#if MAC_SCHED_HW_DEBUG
iot_printf("MAC_SCHED_HW ---%s--- CFG_BEACON_ADDR %x\n", __FUNCTION__,
value);
#endif
}
void mac_sched_set_recursive_mode(mac_vdev_t *vdev, uint8_t enable)
{
uint32_t value;
(void)vdev;
value = RGF_HWQ_READ_REG(CFG_SCHEDULE_ADDR);
value &= ~CFG_SCH_SELF_RECUR_EN_MASK;
value |= ((uint32_t)(enable & 0x01)) << CFG_SCH_SELF_RECUR_EN_OFFSET;
RGF_HWQ_WRITE_REG(CFG_SCHEDULE_ADDR, value);
#if MAC_SCHED_HW_DEBUG
iot_printf("MAC_SCHED_HW ---%s--- CFG_SCHEDULE_ADDR %x\n", __FUNCTION__,
value);
#endif
}
/* config start ntb of next beacon period */
void mac_sched_set_bp_start_ntb(mac_vdev_t *vdev,
uint32_t start_ntb)
{
(void)vdev;
#if ENA_WAR_440
g_phy_cpu_share_ctxt.bcn_start_update_ntb = \
RGF_MAC_READ_REG(CFG_RD_NTB_ADDR);
#endif
RGF_MAC_WRITE_REG(CFG_BCN_START_NTB_ADDR, start_ntb);
#if MAC_SCHED_HW_DEBUG
iot_printf("MAC_SCHED_HW ---%s--- CFG_BCN_START_NTB_ADDR %x\n",
__FUNCTION__, start_ntb);
#endif
}
uint32_t mac_sched_get_bp_start_ntb()
{
return RGF_MAC_READ_REG(CFG_BCN_START_NTB_ADDR);
}
uint32_t mac_sched_get_bp_dur()
{
uint32_t tmp = RGF_MAC_READ_REG(CFG_BEACON_PERIOD_ADDR);
return REG_FIELD_GET(CFG_BEACON_PERIOD, tmp);
}
/* config next beacon period duration */
void mac_sched_set_bp_dur(mac_vdev_t *vdev, uint16_t bp)
{
uint32_t value;
(void)vdev;
value = (((uint32_t)bp) << CFG_BEACON_PERIOD_OFFSET)
& CFG_BEACON_PERIOD_MASK;
RGF_MAC_WRITE_REG(CFG_BEACON_PERIOD_ADDR, value);
#if MAC_SCHED_HW_DEBUG
iot_printf("MAC_SCHED_HW ---%s--- CFG_BEACON_PERIOD_ADDR %x\n",
__FUNCTION__, value);
#endif
}
/* config scheduler of next beacon period */
void mac_sched_set_bp_cmd_list(mac_vdev_t *vdev, hw_sched_cmd_t *cmd,
uint16_t cnt)
{
uint32_t value;
(void)vdev;
value = RGF_HWQ_READ_REG(CFG_SCHEDULE_ADDR);
value &= ~CFG_SCH_CMD_NUM_MASK;
value |= (((uint32_t)cnt << 1) << CFG_SCH_CMD_NUM_OFFSET)
& CFG_SCH_CMD_NUM_MASK;
RGF_HWQ_WRITE_REG(CFG_SCHEDULE_ADDR, value);
RGF_HWQ_WRITE_REG(CFG_SCH_PTR_ADDR, (uint32_t)cmd);
#if MAC_SCHED_HW_DEBUG
iot_printf("MAC_SCHED_HW ---%s--- CFG_SCHEDULE_ADDR %x\n", __FUNCTION__,
value);
iot_printf("MAC_SCHED_HW ---%s--- CFG_SCH_PTR_ADDR %x\n", __FUNCTION__,
cmd);
#endif
}
/* get cmd list cnt being used by HW */
uint8_t mac_sched_get_cmd_list_cnt(mac_vdev_t *vdev)
{
uint32_t value;
(void)vdev;
value = RGF_HWQ_READ_REG(CFG_SCHEDULE_ADDR);
value &= SCH_CUR_NUM_MASK;
value >>= SCH_CUR_NUM_OFFSET;
return (uint8_t)value;
}
/* write trigger for the next beacon period */
void mac_sched_trigger_bp(mac_vdev_t *vdev)
{
uint32_t value;
(void)vdev;
value = RGF_HWQ_READ_REG(CFG_SCHEDULE_ADDR);
if (value & SCH_WR_TRIG_ENABLE_MASK) {
value |= 1 << CFG_SCH_WR_TRIG_OFFSET;
RGF_HWQ_WRITE_REG(CFG_SCHEDULE_ADDR, value);
} else {
IOT_ASSERT(0);
}
#if MAC_SCHED_HW_DEBUG
iot_printf("MAC_SCHED_HW ---%s--- CFG_SCHEDULE_ADDR %x\n", __FUNCTION__,
value);
#endif
}
#define SCHED_ENABLE_SYNC_MAX_LOOP 0xFFFFFFFE
/* enable or disable scheduler of next beacon period */
void mac_sched_enable_bp(mac_vdev_t *vdev, uint8_t enable)
{
uint32_t i, value;
(void)vdev;
value = RGF_HWQ_READ_REG(CFG_SCHEDULE_ADDR);
/* check if HW scheduler already enabled or disabled */
if ((!!(value & SCH_STATUS_MASK)) == (!!enable))
return;
/* enable or disable HW scheduler */
value &= ~CFG_SCH_EN_MASK;
value |= (((uint32_t)(enable & 0x01)) << CFG_SCH_EN_OFFSET)
& CFG_SCH_EN_MASK;
#if DEBUG_HWQ_SHCED_HANG
/* WAR: if TX in progress, stop SCH would make hw tx hang
* to avoid this happen, we need to poll till we're not
* in TX process
*/
if (!!enable == 0) {
/* if to disable SCH */
uint32_t iscco = (PLC_DEV_ROLE_CCO == mac_vdev_cfg_get_node_role(vdev));
mac_tx_wait_all_queue_idle(iscco, MAC_WAIT_TX_IDLE_TIEM_MS);
}
#endif
RGF_HWQ_WRITE_REG(CFG_SCHEDULE_ADDR, value);
#if MAC_SCHED_HW_DEBUG
iot_printf("MAC_SCHED_HW ---%s--- CFG_SCHEDULE_ADDR %x\n", __FUNCTION__,
value);
#endif
#if DEBUG_HWQ_SHCED_HANG
uint32_t debug_cnt = 0;
#endif
if (!mac_txq_is_dbg_mode()) {
/* if sch mode */
/* make sure the HW scheduler status is in sync */
for (i = 0; i < SCHED_ENABLE_SYNC_MAX_LOOP; i++) {
value = RGF_HWQ_READ_REG(CFG_SCHEDULE_ADDR);
if ((!!(value & SCH_STATUS_MASK)) == (!!enable)) {
break;
}
if (i & 0x100) {
/* try to enable or disable again */
REG_FIELD_SET(CFG_SCH_EN, value, enable & 0x01);
RGF_HWQ_WRITE_REG(CFG_SCHEDULE_ADDR, value);
}
#if DEBUG_HWQ_SHCED_HANG
debug_cnt++;
if(debug_cnt > MAX_WAIT_DUMP_CNT)
{
mac_dump_buf(MAC_DUMP_TYPE_5, (uint32_t *)mac_txq_get_curr_desc_ptr(\
PLC_DEV_ROLE_CCO == mac_vdev_cfg_get_node_role(vdev), \
false), \
sizeof(tx_mpdu_start)/sizeof(uint32_t), \
(uint32_t *)RGF_HWQ_BASEADDR, 108, \
(uint32_t *)(PHY_BASEADDR + CFG_BB_RX_TD_DBG_BUS0_ADDR), 38, \
true);
}
#endif
}
if (i >= SCHED_ENABLE_SYNC_MAX_LOOP)
IOT_ASSERT(0);
}
if (!enable) {
/* clear schduler command list if scheduler disabled */
value = RGF_HWQ_READ_REG(CFG_SCHEDULE_ADDR);
value |= CFG_SCH_CLR_MASK;
RGF_HWQ_WRITE_REG(CFG_SCHEDULE_ADDR, value);
}
}
#endif /* HW_PLATFORM >= HW_PLATFORM_FPGA */