@@ -625,6 +625,232 @@ static void rtw89_mcc_assign_pattern(struct rtw89_dev *rtwdev,
pattern->courtesy.slot_num);
}
+/* The follow-up roughly shows the relationship between the parameters
+ * for pattern calculation.
+ *
+ * |< duration ref >| (if mid bt) |< duration aux >|
+ * |< tob ref >|< toa ref >| ... |< tob aux >|< toa aux >|
+ * V V
+ * tbtt ref tbtt aux
+ * |< beacon offset >|
+ *
+ * In loose pattern calculation, we only ensure at least tob_ref and
+ * toa_ref have positive results. If tob_aux or toa_aux is negative
+ * unfortunately, FW will be notified to handle it with courtesy
+ * mechanism.
+ */
+static void __rtw89_mcc_calc_pattern_loose(struct rtw89_dev *rtwdev,
+ struct rtw89_mcc_pattern *ptrn,
+ bool hdl_bt)
+{
+ struct rtw89_mcc_info *mcc = &rtwdev->mcc;
+ struct rtw89_mcc_role *ref = &mcc->role_ref;
+ struct rtw89_mcc_role *aux = &mcc->role_aux;
+ struct rtw89_mcc_config *config = &mcc->config;
+ u16 bcn_ofst = config->beacon_offset;
+ u16 bt_dur_in_mid = 0;
+ u16 max_bcn_ofst;
+ s16 upper, lower;
+ u16 res;
+
+ *ptrn = (typeof(*ptrn)){
+ .plan = hdl_bt ? RTW89_MCC_PLAN_TAIL_BT : RTW89_MCC_PLAN_NO_BT,
+ };
+
+ if (!hdl_bt)
+ goto calc;
+
+ max_bcn_ofst = ref->duration + aux->duration;
+ if (ref->limit.enable)
+ max_bcn_ofst = min_t(u16, max_bcn_ofst,
+ ref->limit.max_toa + aux->duration);
+ else if (aux->limit.enable)
+ max_bcn_ofst = min_t(u16, max_bcn_ofst,
+ ref->duration + aux->limit.max_tob);
+
+ if (bcn_ofst > max_bcn_ofst && bcn_ofst >= mcc->bt_role.duration) {
+ bt_dur_in_mid = mcc->bt_role.duration;
+ ptrn->plan = RTW89_MCC_PLAN_MID_BT;
+ }
+
+calc:
+ rtw89_debug(rtwdev, RTW89_DBG_CHAN,
+ "MCC calc ptrn_ls: plan %d, bcn_ofst %d\n",
+ ptrn->plan, bcn_ofst);
+
+ res = bcn_ofst - bt_dur_in_mid;
+ upper = min_t(s16, ref->duration, res);
+ lower = 0;
+
+ if (ref->limit.enable) {
+ upper = min_t(s16, upper, ref->limit.max_toa);
+ lower = max_t(s16, lower, ref->duration - ref->limit.max_tob);
+ } else if (aux->limit.enable) {
+ upper = min_t(s16, upper,
+ res - (aux->duration - aux->limit.max_toa));
+ lower = max_t(s16, lower, res - aux->limit.max_tob);
+ }
+
+ if (lower < upper)
+ ptrn->toa_ref = (upper + lower) / 2;
+ else
+ ptrn->toa_ref = lower;
+
+ ptrn->tob_ref = ref->duration - ptrn->toa_ref;
+ ptrn->tob_aux = res - ptrn->toa_ref;
+ ptrn->toa_aux = aux->duration - ptrn->tob_aux;
+}
+
+/* In strict pattern calculation, we consider timing that might need
+ * for HW stuffs, i.e. min_tob and min_toa.
+ */
+static int __rtw89_mcc_calc_pattern_strict(struct rtw89_dev *rtwdev,
+ struct rtw89_mcc_pattern *ptrn)
+{
+ struct rtw89_mcc_info *mcc = &rtwdev->mcc;
+ struct rtw89_mcc_role *ref = &mcc->role_ref;
+ struct rtw89_mcc_role *aux = &mcc->role_aux;
+ struct rtw89_mcc_config *config = &mcc->config;
+ u16 min_tob = RTW89_MCC_EARLY_RX_BCN_TIME;
+ u16 min_toa = RTW89_MCC_MIN_RX_BCN_TIME;
+ u16 bcn_ofst = config->beacon_offset;
+ s16 upper_toa_ref, lower_toa_ref;
+ s16 upper_tob_aux, lower_tob_aux;
+ u16 bt_dur_in_mid;
+ s16 res;
+
+ rtw89_debug(rtwdev, RTW89_DBG_CHAN,
+ "MCC calc ptrn_st: plan %d, bcn_ofst %d\n",
+ ptrn->plan, bcn_ofst);
+
+ if (ptrn->plan == RTW89_MCC_PLAN_MID_BT)
+ bt_dur_in_mid = mcc->bt_role.duration;
+ else
+ bt_dur_in_mid = 0;
+
+ if (ref->duration < min_tob + min_toa) {
+ rtw89_debug(rtwdev, RTW89_DBG_CHAN,
+ "MCC calc ptrn_st: not meet ref dur cond\n");
+ return -EINVAL;
+ }
+
+ if (aux->duration < min_tob + min_toa) {
+ rtw89_debug(rtwdev, RTW89_DBG_CHAN,
+ "MCC calc ptrn_st: not meet aux dur cond\n");
+ return -EINVAL;
+ }
+
+ res = bcn_ofst - min_toa - min_tob - bt_dur_in_mid;
+ if (res < 0) {
+ rtw89_debug(rtwdev, RTW89_DBG_CHAN,
+ "MCC calc ptrn_st: not meet bcn_ofst cond\n");
+ return -EINVAL;
+ }
+
+ upper_toa_ref = min_t(s16, min_toa + res, ref->duration - min_tob);
+ lower_toa_ref = min_toa;
+ upper_tob_aux = min_t(s16, min_tob + res, aux->duration - min_toa);
+ lower_tob_aux = min_tob;
+
+ if (ref->limit.enable) {
+ if (min_tob > ref->limit.max_tob || min_toa > ref->limit.max_toa) {
+ rtw89_debug(rtwdev, RTW89_DBG_CHAN,
+ "MCC calc ptrn_st: conflict ref limit\n");
+ return -EINVAL;
+ }
+
+ upper_toa_ref = min_t(s16, upper_toa_ref, ref->limit.max_toa);
+ lower_toa_ref = max_t(s16, lower_toa_ref,
+ ref->duration - ref->limit.max_tob);
+ } else if (aux->limit.enable) {
+ if (min_tob > aux->limit.max_tob || min_toa > aux->limit.max_toa) {
+ rtw89_debug(rtwdev, RTW89_DBG_CHAN,
+ "MCC calc ptrn_st: conflict aux limit\n");
+ return -EINVAL;
+ }
+
+ upper_tob_aux = min_t(s16, upper_tob_aux, aux->limit.max_tob);
+ lower_tob_aux = max_t(s16, lower_tob_aux,
+ aux->duration - aux->limit.max_toa);
+ }
+
+ upper_toa_ref = min_t(s16, upper_toa_ref,
+ bcn_ofst - bt_dur_in_mid - lower_tob_aux);
+ lower_toa_ref = max_t(s16, lower_toa_ref,
+ bcn_ofst - bt_dur_in_mid - upper_tob_aux);
+ if (lower_toa_ref > upper_toa_ref) {
+ rtw89_debug(rtwdev, RTW89_DBG_CHAN,
+ "MCC calc ptrn_st: conflict boundary\n");
+ return -EINVAL;
+ }
+
+ ptrn->toa_ref = (upper_toa_ref + lower_toa_ref) / 2;
+ ptrn->tob_ref = ref->duration - ptrn->toa_ref;
+ ptrn->tob_aux = bcn_ofst - ptrn->toa_ref - bt_dur_in_mid;
+ ptrn->toa_aux = aux->duration - ptrn->tob_aux;
+ return 0;
+}
+
+static int rtw89_mcc_calc_pattern(struct rtw89_dev *rtwdev, bool hdl_bt)
+{
+ struct rtw89_mcc_info *mcc = &rtwdev->mcc;
+ struct rtw89_mcc_role *ref = &mcc->role_ref;
+ struct rtw89_mcc_role *aux = &mcc->role_aux;
+ bool sel_plan[NUM_OF_RTW89_MCC_PLAN] = {};
+ struct rtw89_mcc_pattern ptrn;
+ int ret;
+ int i;
+
+ if (ref->limit.enable && aux->limit.enable) {
+ rtw89_debug(rtwdev, RTW89_DBG_CHAN,
+ "MCC calc ptrn: not support dual limited roles\n");
+ return -EINVAL;
+ }
+
+ if (ref->limit.enable &&
+ ref->duration > ref->limit.max_tob + ref->limit.max_toa) {
+ rtw89_debug(rtwdev, RTW89_DBG_CHAN,
+ "MCC calc ptrn: not fit ref limit\n");
+ return -EINVAL;
+ }
+
+ if (aux->limit.enable &&
+ aux->duration > aux->limit.max_tob + aux->limit.max_toa) {
+ rtw89_debug(rtwdev, RTW89_DBG_CHAN,
+ "MCC calc ptrn: not fit aux limit\n");
+ return -EINVAL;
+ }
+
+ if (hdl_bt) {
+ sel_plan[RTW89_MCC_PLAN_TAIL_BT] = true;
+ sel_plan[RTW89_MCC_PLAN_MID_BT] = true;
+ } else {
+ sel_plan[RTW89_MCC_PLAN_NO_BT] = true;
+ }
+
+ for (i = 0; i < NUM_OF_RTW89_MCC_PLAN; i++) {
+ if (!sel_plan[i])
+ continue;
+
+ ptrn = (typeof(ptrn)){
+ .plan = i,
+ };
+
+ ret = __rtw89_mcc_calc_pattern_strict(rtwdev, &ptrn);
+ if (ret)
+ rtw89_debug(rtwdev, RTW89_DBG_CHAN,
+ "MCC calc ptrn_st with plan %d: fail\n", i);
+ else
+ goto done;
+ }
+
+ __rtw89_mcc_calc_pattern_loose(rtwdev, &ptrn, hdl_bt);
+
+done:
+ rtw89_mcc_assign_pattern(rtwdev, &ptrn);
+ return 0;
+}
+
static void rtw89_mcc_set_default_pattern(struct rtw89_dev *rtwdev)
{
struct rtw89_mcc_info *mcc = &rtwdev->mcc;
@@ -956,6 +1182,7 @@ static int rtw89_mcc_fill_config(struct rtw89_dev *rtwdev)
struct rtw89_mcc_role *aux = &mcc->role_aux;
struct rtw89_mcc_config *config = &mcc->config;
bool hdl_bt;
+ int ret;
memset(config, 0, sizeof(*config));
@@ -985,7 +1212,13 @@ static int rtw89_mcc_fill_config(struct rtw89_dev *rtwdev)
hdl_bt = rtw89_mcc_duration_decision_on_bt(rtwdev);
rtw89_debug(rtwdev, RTW89_DBG_CHAN, "MCC handle bt: %d\n", hdl_bt);
+ ret = rtw89_mcc_calc_pattern(rtwdev, hdl_bt);
+ if (!ret)
+ goto bottom;
+
rtw89_mcc_set_default_pattern(rtwdev);
+
+bottom:
return rtw89_mcc_fill_start_tsf(rtwdev);
}
@@ -4403,6 +4403,8 @@ enum rtw89_mcc_plan {
RTW89_MCC_PLAN_TAIL_BT,
RTW89_MCC_PLAN_MID_BT,
RTW89_MCC_PLAN_NO_BT,
+
+ NUM_OF_RTW89_MCC_PLAN,
};
struct rtw89_mcc_pattern {