/**************************************************************************** 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 "multi_nid_sync.h" #include "phy_bb.h" #include "iot_errno_api.h" #include "plc_const.h" #include "iot_io.h" #include "mac_sched_hw.h" #include "plc_utils.h" #include "mac_pdev.h" #include "phy_multi_ppm.h" #include "mac_vdev.h" #include "hw_war.h" #include "mac_cmn_hw.h" #include "plc_protocol.h" #if ENA_SYNC_DIFF_CCO_PPM #define mac_multi_sync_ctxt_get(pdev_id) \ (((pdev_id) < MAX_PDEV_NUM) ? (&g_mac_pdev[pdev_id]->ppm_cal) : NULL) static uint32_t mac_multi_sync_get_nid(m_ppm_cal_ctxt_t *ctxt, uint8_t idx) { if (idx >= MAX_SYNC_CCO_CNT || ctxt == NULL) { IOT_ASSERT(0); } return ctxt->ppm_cal[idx].nid; } static void mac_multi_sync_set_nid(m_ppm_cal_ctxt_t *ctxt, uint8_t idx, nid_t nid) { if (idx >= MAX_SYNC_CCO_CNT || ctxt == NULL) { IOT_ASSERT(0); } ctxt->ppm_cal[idx].nid = nid; return; } static uint32_t mac_multi_sync_get_flag(m_ppm_cal_ctxt_t *ctxt, uint8_t idx) { (void)ctxt; phy_ppm_status_ctxt_t *p_ctxt = phy_multi_sync_get_ppm_status(); return phy_multi_sync_get_valid_flag(p_ctxt, idx); } static void mac_multi_sync_ppm_rst(m_ppm_cal_ctxt_t *ctxt) { ctxt->last_clear_ntb = 0; ctxt->phy_ppm_ctxt = phy_multi_sync_get_ppm_status(); for (uint8_t idx = 0; idx < MAX_SYNC_CCO_CNT; idx++) { ctxt->ppm_cal[idx].rsv = 0; ctxt->ppm_cal[idx].nid = PLC_NID_INVALID; ctxt->ppm_cal[idx].ntb_record.last_bcn_local_ts = 0; ctxt->ppm_cal[idx].ntb_record.last_bcn_remote_tx_ntb = 0; ctxt->ppm_cal[idx].ntb_record.last_saved_ntb = 0; ctxt->ppm_cal[idx].ntb_record.last_bcn_local_ntb = 0; } } uint8_t IRAM_ATTR mac_multi_sync_get_ena(uint8_t pdev_id) { m_ppm_cal_ctxt_t *ctxt = mac_multi_sync_ctxt_get(pdev_id); if (NULL == ctxt) { return 0; } phy_ppm_status_ctxt_t *status_ctxt = (phy_ppm_status_ctxt_t *)ctxt->phy_ppm_ctxt; IOT_ASSERT(status_ctxt); return status_ctxt->enable; } void mac_multi_sync_set_ena(uint8_t pdev_id, uint8_t enable_flag) { m_ppm_cal_ctxt_t *ctxt = mac_multi_sync_ctxt_get(pdev_id); phy_ppm_status_ctxt_t *status_ctxt = (phy_ppm_status_ctxt_t*)ctxt->phy_ppm_ctxt; IOT_ASSERT(ctxt != NULL && status_ctxt != NULL); enable_flag = (enable_flag == 0) ? 0 : 1; if (enable_flag == mac_multi_sync_get_ena(pdev_id)) { return; } phy_multi_sync_set_enable(status_ctxt, enable_flag); if (enable_flag == 0) { mac_multi_sync_ppm_rst(ctxt); phy_multi_sync_ppm_rst(status_ctxt); } iot_printf("[M-NID]multi sync ena=%d\n", enable_flag); } int32_t mac_multi_sync_get_ppm(uint8_t pdev_id, nid_t nid) { phy_ppm_status_ctxt_t *ctxt = phy_multi_sync_get_ppm_status(); uint8_t idx = phy_multi_sync_get_record_idx(ctxt, nid); (void)pdev_id; if (idx < MAX_SYNC_CCO_CNT) { return phy_multi_sync_get_record_ppm(ctxt, idx); } return PLC_MAX_MAC_NTB_PPM; } void mac_multi_sync_init(uint8_t pdev_id) { phy_ppm_status_ctxt_t *status_ctxt; m_ppm_cal_ctxt_t *ctxt = mac_multi_sync_ctxt_get(pdev_id); if (NULL == ctxt) { return; } ctxt->phy_ppm_ctxt = phy_multi_sync_get_ppm_status(); status_ctxt = (phy_ppm_status_ctxt_t*)ctxt->phy_ppm_ctxt; mac_multi_sync_ppm_rst(ctxt); phy_multi_sync_ppm_rst(status_ctxt); phy_multi_sync_set_enable(status_ctxt, ENA_SYNC_DIFF_CCO_PPM); /* nid=0 ppm set mp ppms */ phy_multi_sync_set_record_ppm(status_ctxt, 0, (-g_phy_ctxt.dep.phy_cal_ppm_in_oem) << PLC_NTB_PPM_SHIFT); /* set valid flag */ phy_multi_sync_set_valid_flag(status_ctxt, 0, RECORD_PPM_VALID); #if HW_PLATFORM >= HW_PLATFORM_FPGA /* enable hw tx/rx multippm sync */ phy_set_sw_nn_rx_ppm_en(ENA_SYNC_DIFF_CCO_PPM); phy_set_sw_nn_tx_ppm_en(ENA_SYNC_DIFF_CCO_PPM); /* if hw ppm, set hw ppm */ phy_set_sw_nn_ppm_para(0, PLC_NID_INVALID, -g_phy_ctxt.dep.phy_cal_ppm_in_oem); #endif } uint32_t mac_multi_ppm_record(uint8_t pdev_id, nid_t nid, nid_t my_nid, uint32_t rate_mode, uint32_t r_ts, uint32_t l_ts, uint32_t l_ntb, uint32_t min_update_period, int16_t ppm_err) { m_ppm_cal_ctxt_t *cal_ctxt = mac_multi_sync_ctxt_get(pdev_id); phy_ppm_status_ctxt_t *status_ctxt = (phy_ppm_status_ctxt_t *)cal_ctxt->phy_ppm_ctxt; uint32_t ret = ERR_FAIL; uint8_t final_idx = MAX_SYNC_CCO_CNT; uint32_t tmp_nid; uint32_t valid; mac_ntb_record_ctxt_t tmp_record, *final_record; (void)rate_mode; if ((NULL == cal_ctxt) || (mac_multi_sync_get_ena(pdev_id) == 0)) { return ret; } /* find the place to insert, there's 2 conditions: * 1) nid already saved * 2) nid not saved before * for 1) case, we need use the original entry, * for 2) case, we need to find there's enough place to save new nid. */ for (uint8_t idx = 1; idx < MAX_SYNC_CCO_CNT; idx++) { valid = mac_multi_sync_get_flag(cal_ctxt, idx); tmp_nid = mac_multi_sync_get_nid(cal_ctxt, idx); if (final_idx == MAX_SYNC_CCO_CNT && valid == RECORD_PPM_INVALID_NID) { final_idx = idx; } if (nid == tmp_nid) { final_idx = idx; break; } } if (final_idx != MAX_SYNC_CCO_CNT) { valid = mac_multi_sync_get_flag(cal_ctxt, final_idx); if (valid == RECORD_PPM_INVALID_NID) { mac_multi_sync_set_nid(cal_ctxt, final_idx, nid); phy_multi_sync_set_record_nid(status_ctxt, final_idx, nid); phy_multi_sync_set_valid_flag(status_ctxt, final_idx, RECORD_PPM_INVALID_PPM); } if (my_nid == PLC_NID_INVALID || nid != my_nid) { /* if this nid is not my nid, then cal ppm here */ final_record = &cal_ctxt->ppm_cal[final_idx].ntb_record; if (valid == RECORD_PPM_INVALID_NID) { /* first update */ ppm_err = mac_ppm_cal(final_record, r_ts, l_ts, l_ntb, min_update_period); } else { os_mem_cpy(&tmp_record, final_record, sizeof(tmp_record)); ppm_err = mac_ppm_cal(&tmp_record, r_ts, l_ts, l_ntb, min_update_period); if (IOT_ABS(ppm_err) > PLC_SYNC_NTB_MAX_PPM) { if (valid == RECORD_PPM_INVALID_PPM) { /* set to pib ppm */ ppm_err = -phy_get_cal_ppm_in_pib() << PLC_NTB_PPM_SHIFT; } else { ppm_err = PLC_MAX_MAC_NTB_PPM; /* invalid ppm */ if ((tmp_record.last_saved_ntb - final_record->last_saved_ntb) > MAC_MULTI_UPDATE_TIMEOUT_NTB) { os_mem_cpy(final_record, &tmp_record, sizeof(tmp_record)); } } } else { os_mem_cpy(final_record, &tmp_record, sizeof(tmp_record)); } } } else if (my_nid != PLC_NID_INVALID) { /* if nid is not my nid, then follow 168 line, * just save the local ntb here for aging the ppm status */ cal_ctxt->ppm_cal[final_idx].ntb_record.last_saved_ntb = \ mac_sched_get_ntb64(NULL); } if (ppm_err > -PLC_MAX_MAC_NTB_PPM && ppm_err < PLC_MAX_MAC_NTB_PPM) { /* if ppm_err is valid, save the ppm of the corresponding nid */ phy_multi_sync_set_record_ppm(status_ctxt, final_idx, ppm_err); /* set valid flag */ phy_multi_sync_set_valid_flag(status_ctxt, final_idx, RECORD_PPM_VALID); ret = ERR_OK; } else { ret = ERR_FAIL; } if (valid != RECORD_PPM_VALID) { iot_printf("[M-NID]nid:%x,ppm:%d,flag:%d\n", nid, ppm_err, valid); } } ret = phy_multi_sync_set_ppm(status_ctxt, nid); if (ret != ERR_OK) { iot_printf("ckq set multippm err, nid:0x%x\n", nid); } return ret; } void mac_multi_sync_nid_timeout_clear(uint8_t pdev_id) { m_ppm_cal_ctxt_t *cal_ctxt = mac_multi_sync_ctxt_get(pdev_id); phy_ppm_status_ctxt_t *status_ctxt = (phy_ppm_status_ctxt_t *)cal_ctxt->phy_ppm_ctxt; IOT_ASSERT(cal_ctxt != NULL && status_ctxt != NULL); uint64_t cur_ntb = mac_sched_get_ntb64(NULL); if (mac_multi_sync_get_ena(pdev_id) == 0) { return; } if ((int64_t)(cur_ntb - cal_ctxt->last_clear_ntb) < MAX_BCN_RX_TIMEOUT_NTB) { return; } else { cal_ctxt->last_clear_ntb = cur_ntb; } for (uint8_t idx = 1; idx < MAX_SYNC_CCO_CNT; idx++) { uint32_t cur_nid = cal_ctxt->ppm_cal[idx].nid; if ((cur_nid != PLC_NID_INVALID) && ((int64_t)(cur_ntb - cal_ctxt->ppm_cal[idx].ntb_record.last_saved_ntb) > MAX_BCN_RX_TIMEOUT_NTB)) { iot_printf("[M-NID]aged nid:%x,ppm:%d\n", cal_ctxt->ppm_cal[idx].nid, status_ctxt->ppm_status[idx].ppm_err); cal_ctxt->ppm_cal[idx].rsv = 0; cal_ctxt->ppm_cal[idx].nid = PLC_NID_INVALID; cal_ctxt->ppm_cal[idx].ntb_record.last_bcn_local_ts = 0; cal_ctxt->ppm_cal[idx].ntb_record.last_bcn_remote_tx_ntb = 0; cal_ctxt->ppm_cal[idx].ntb_record.last_saved_ntb = 0; cal_ctxt->ppm_cal[idx].ntb_record.last_bcn_local_ntb = 0; status_ctxt->ppm_status[idx].rsv = 0; status_ctxt->ppm_status[idx].valid = RECORD_PPM_INVALID_NID; status_ctxt->ppm_status[idx].nid = PLC_NID_INVALID; status_ctxt->ppm_status[idx].ppm_err = 0; phy_set_sw_nn_ppm_para(idx, PLC_NID_INVALID, 0); } } } static uint32_t mac_ntb_convert(uint8_t pdev_id, nid_t nid, uint32_t *l_ntb_array, uint32_t l_ntb_len, uint32_t *r_ntb_array, uint32_t r_ntb_len) { uint16_t ret = ERR_FAIL; int16_t delta_ppm = 0; int16_t my_ppm = 0; int16_t other_ppm = 0; uint32_t last_bcn_local_ntb = 0; uint32_t last_bcn_remote_tx_ntb = 0; uint32_t tmp_idx; uint32_t *tmp_ptr; int32_t tmp; int64_t delta_ntb; nid_t my_nid; mac_pdev_t *pdev = get_pdev_ptr(pdev_id); mac_vdev_t *vdev = pdev->vdev[PLC_DEFAULT_VDEV]; uint64_t curr_ntb = mac_sched_get_ntb64(NULL); uint64_t last_bcn_local_ntb64 = 0; m_ppm_cal_ctxt_t *cal_ctxt = &pdev->ppm_cal; phy_ppm_status_ctxt_t *status_ctxt = \ (phy_ppm_status_ctxt_t *)cal_ctxt->phy_ppm_ctxt; IOT_ASSERT(cal_ctxt != NULL && status_ctxt != NULL); vdev_get_nid(vdev, &my_nid); if (mac_multi_sync_get_ena(pdev_id) == 0) { return ret; } if (my_nid == nid) { return ret; } for (uint8_t idx = 1; idx < MAX_SYNC_CCO_CNT; idx++) { if (mac_multi_sync_get_flag(cal_ctxt, idx) != RECORD_PPM_VALID) { continue; } uint32_t cur_nid = status_ctxt->ppm_status[idx].nid; if (nid == cur_nid) { last_bcn_local_ntb = \ cal_ctxt->ppm_cal[idx].ntb_record.last_bcn_local_ntb; last_bcn_remote_tx_ntb = \ cal_ctxt->ppm_cal[idx].ntb_record.last_bcn_remote_tx_ntb; last_bcn_local_ntb64 = \ cal_ctxt->ppm_cal[idx].ntb_record.last_saved_ntb; other_ppm = phy_multi_sync_get_record_ppm(status_ctxt, idx); if (other_ppm != PLC_MAX_MAC_NTB_PPM) { ret = ERR_OK; } } if (my_nid != PLC_NID_INVALID && my_nid == cur_nid) { my_ppm = phy_multi_sync_get_record_ppm(status_ctxt, idx); } } if (ret == ERR_OK) { /* if the last_bcn_local_ntb64 is far from curr_ntb, cannot be converted */ if (curr_ntb - last_bcn_local_ntb64 > (UINT32_MAX / 2)) { ret = ERR_FAIL; goto out; } if (l_ntb_array != NULL) { delta_ppm = other_ppm - my_ppm; tmp_ptr = l_ntb_array; for (tmp_idx = 0; tmp_idx < l_ntb_len; tmp_idx++) { tmp = (int32_t)(*(tmp_ptr + tmp_idx) - last_bcn_local_ntb); delta_ntb = tmp; delta_ntb = (delta_ntb << (PPM_CALC_MILLION_SHIFT + PLC_NTB_PPM_SHIFT)) / ((1 << (PPM_CALC_MILLION_SHIFT + PLC_NTB_PPM_SHIFT)) + delta_ppm); *(tmp_ptr + tmp_idx) = (uint32_t)(last_bcn_remote_tx_ntb + delta_ntb); } } if (r_ntb_array != NULL) { delta_ppm = my_ppm - other_ppm; tmp_ptr = r_ntb_array; for (tmp_idx = 0; tmp_idx < r_ntb_len; tmp_idx++) { tmp = (int32_t)(*(tmp_ptr + tmp_idx) - last_bcn_remote_tx_ntb); delta_ntb = tmp; delta_ntb = (delta_ntb << (PPM_CALC_MILLION_SHIFT + PLC_NTB_PPM_SHIFT)) / ((1 << (PPM_CALC_MILLION_SHIFT + PLC_NTB_PPM_SHIFT)) + delta_ppm); *(tmp_ptr + tmp_idx) = (uint32_t)(last_bcn_local_ntb + delta_ntb); } } } out: return ret; } uint32_t mac_myntb_to_other_ntb(uint8_t pdev_id, nid_t nid, uint32_t *array, uint32_t len) { uint32_t ret = ERR_FAIL; if (mac_multi_sync_get_ena(pdev_id) == 0) { return ret; } ret = mac_ntb_convert(pdev_id, nid, array, len, NULL, 0); return ret; } uint32_t mac_other_ntb_to_myntb(uint8_t pdev_id, nid_t nid, uint32_t *array, uint32_t len) { uint32_t ret = ERR_FAIL; if (mac_multi_sync_get_ena(pdev_id) == 0) { return ret; } ret = mac_ntb_convert(pdev_id, nid, NULL, 0, array, len); return ret; } void mac_multi_sync_apply_new_ppm_step(uint8_t pdev_id, nid_t nid, int8_t ppm_step) { phy_ppm_status_ctxt_t *ppm_sts_ctxt = NULL; uint32_t ppm_sync_valid = 0; int16_t ppm_oem; uint8_t idx; (void)pdev_id; if (nid != PLC_DBG_PKT_MODE_NID) { return; } ppm_sts_ctxt = phy_multi_sync_get_ppm_status(); idx = phy_multi_sync_get_record_idx(ppm_sts_ctxt, nid); ppm_sync_valid = phy_multi_sync_get_valid_flag(ppm_sts_ctxt, idx); ppm_oem = phy_get_cal_ppm_in_pib(); if (ppm_sync_valid == RECORD_PPM_VALID) { phy_multi_sync_set_record_ppm(ppm_sts_ctxt, idx, (-ppm_oem + ppm_step) << PLC_NTB_PPM_SHIFT); phy_multi_sync_set_ppm(ppm_sts_ctxt, nid); #if ENA_SW_SYNC_PPM_WAR || ENA_HW_SYNC_PPM_WAR g_phy_ctxt.dep.phy_ppm_init = (ppm_oem - ppm_step) << PLC_REG_PPM_SHIFT; g_phy_cpu_share_ctxt.phy_ppm_init = g_phy_ctxt.dep.phy_ppm_init; #endif } } #endif uint8_t mac_multi_sync_get_ppm_tbl(pdevid_t pdev_id, plc_ppm_status_t *ppm_tbl) { uint8_t vaild_nid_cnt = 0; if (!mac_multi_sync_get_ena(pdev_id)) { BUILD_BUG_ON(PLC_MAX_NEIGHBOR_NETWORK >= 2); uint32_t self_nid = mac_get_hw_nid(); if (self_nid != PLC_NID_INVALID) { ppm_tbl[vaild_nid_cnt].nid = 0; ppm_tbl[vaild_nid_cnt].ppm_err = phy_txppm_get(); vaild_nid_cnt++; ppm_tbl[vaild_nid_cnt].nid = self_nid; ppm_tbl[vaild_nid_cnt].ppm_err = phy_txppm_get(); vaild_nid_cnt++; } else { ppm_tbl[vaild_nid_cnt].nid = 0; ppm_tbl[vaild_nid_cnt].ppm_err = -phy_get_cal_ppm_in_pib(); vaild_nid_cnt++; } return vaild_nid_cnt; } phy_ppm_status_ctxt_t *ctxt = phy_multi_sync_get_ppm_status(); for (uint8_t idx = 0; idx < MAX_SYNC_CCO_CNT && vaild_nid_cnt < PLC_MAX_NEIGHBOR_NETWORK; idx++) { if (!phy_multi_sync_get_valid_flag(ctxt, idx)) { continue; } ppm_tbl[vaild_nid_cnt].nid = phy_multi_sync_get_record_nid(ctxt, idx); ppm_tbl[vaild_nid_cnt].ppm_err = phy_multi_sync_get_record_ppm(ctxt, idx) >> PLC_NTB_PPM_SHIFT; vaild_nid_cnt++; } (void)pdev_id; (void)ctxt; return vaild_nid_cnt; }