diff options
Diffstat (limited to 'drivers/net/wireless/realtek/rtw89/acpi.c')
-rw-r--r-- | drivers/net/wireless/realtek/rtw89/acpi.c | 1037 |
1 files changed, 1013 insertions, 24 deletions
diff --git a/drivers/net/wireless/realtek/rtw89/acpi.c b/drivers/net/wireless/realtek/rtw89/acpi.c index f5dedb12c129..581d6d4154d3 100644 --- a/drivers/net/wireless/realtek/rtw89/acpi.c +++ b/drivers/net/wireless/realtek/rtw89/acpi.c @@ -12,6 +12,121 @@ static const guid_t rtw89_guid = GUID_INIT(0xD2A8C3E8, 0x4B69, 0x4F00, 0x82, 0xBD, 0xFE, 0x86, 0x07, 0x80, 0x3A, 0xA7); +static u32 rtw89_acpi_traversal_object(struct rtw89_dev *rtwdev, + const union acpi_object *obj, u8 *pos) +{ + const union acpi_object *elm; + unsigned int i; + u32 sub_len; + u32 len = 0; + u8 *tmp; + + switch (obj->type) { + case ACPI_TYPE_INTEGER: + if (pos) + pos[len] = obj->integer.value; + + len++; + break; + case ACPI_TYPE_BUFFER: + if (unlikely(obj->buffer.length == 0)) { + rtw89_debug(rtwdev, RTW89_DBG_ACPI, + "%s: invalid buffer type\n", __func__); + goto err; + } + + if (pos) + memcpy(pos, obj->buffer.pointer, obj->buffer.length); + + len += obj->buffer.length; + break; + case ACPI_TYPE_PACKAGE: + if (unlikely(obj->package.count == 0)) { + rtw89_debug(rtwdev, RTW89_DBG_ACPI, + "%s: invalid package type\n", __func__); + goto err; + } + + for (i = 0; i < obj->package.count; i++) { + elm = &obj->package.elements[i]; + tmp = pos ? pos + len : NULL; + + sub_len = rtw89_acpi_traversal_object(rtwdev, elm, tmp); + if (unlikely(sub_len == 0)) + goto err; + + len += sub_len; + } + break; + default: + rtw89_debug(rtwdev, RTW89_DBG_ACPI, "%s: unhandled type: %d\n", + __func__, obj->type); + goto err; + } + + return len; + +err: + return 0; +} + +static u32 rtw89_acpi_calculate_object_length(struct rtw89_dev *rtwdev, + const union acpi_object *obj) +{ + return rtw89_acpi_traversal_object(rtwdev, obj, NULL); +} + +static struct rtw89_acpi_data * +rtw89_acpi_evaluate_method(struct rtw89_dev *rtwdev, const char *method) +{ + struct acpi_buffer buf = {ACPI_ALLOCATE_BUFFER, NULL}; + struct rtw89_acpi_data *data = NULL; + acpi_handle root, handle; + union acpi_object *obj; + acpi_status status; + u32 len; + + root = ACPI_HANDLE(rtwdev->dev); + if (!root) { + rtw89_debug(rtwdev, RTW89_DBG_ACPI, + "acpi (%s): failed to get root\n", method); + return NULL; + } + + status = acpi_get_handle(root, (acpi_string)method, &handle); + if (ACPI_FAILURE(status)) { + rtw89_debug(rtwdev, RTW89_DBG_ACPI, + "acpi (%s): failed to get handle\n", method); + return NULL; + } + + status = acpi_evaluate_object(handle, NULL, NULL, &buf); + if (ACPI_FAILURE(status)) { + rtw89_debug(rtwdev, RTW89_DBG_ACPI, + "acpi (%s): failed to evaluate object\n", method); + return NULL; + } + + obj = buf.pointer; + len = rtw89_acpi_calculate_object_length(rtwdev, obj); + if (unlikely(len == 0)) { + rtw89_debug(rtwdev, RTW89_DBG_ACPI, + "acpi (%s): failed to traversal obj len\n", method); + goto out; + } + + data = kzalloc(struct_size(data, buf, len), GFP_KERNEL); + if (!data) + goto out; + + data->len = len; + rtw89_acpi_traversal_object(rtwdev, obj, data->buf); + +out: + ACPI_FREE(obj); + return data; +} + static int rtw89_acpi_dsm_get_value(struct rtw89_dev *rtwdev, union acpi_object *obj, u8 *value) @@ -121,6 +236,49 @@ int rtw89_acpi_dsm_get_policy_6ghz_sp(struct rtw89_dev *rtwdev, return 0; } +static bool chk_acpi_policy_tas_sig(const struct rtw89_acpi_policy_tas *p) +{ + return p->signature[0] == 0x52 && + p->signature[1] == 0x54 && + p->signature[2] == 0x4B && + p->signature[3] == 0x05; +} + +static int rtw89_acpi_dsm_get_policy_tas(struct rtw89_dev *rtwdev, + union acpi_object *obj, + struct rtw89_acpi_policy_tas **policy) +{ + const struct rtw89_acpi_policy_tas *ptr; + u32 buf_len; + + if (obj->type != ACPI_TYPE_BUFFER) { + rtw89_debug(rtwdev, RTW89_DBG_ACPI, + "acpi: expect buffer but type: %d\n", obj->type); + return -EINVAL; + } + + buf_len = obj->buffer.length; + if (buf_len < sizeof(*ptr)) { + rtw89_debug(rtwdev, RTW89_DBG_ACPI, "%s: invalid buffer length: %u\n", + __func__, buf_len); + return -EINVAL; + } + + ptr = (typeof(ptr))obj->buffer.pointer; + if (!chk_acpi_policy_tas_sig(ptr)) { + rtw89_debug(rtwdev, RTW89_DBG_ACPI, "%s: bad signature\n", __func__); + return -EINVAL; + } + + *policy = kmemdup(ptr, sizeof(*ptr), GFP_KERNEL); + if (!*policy) + return -ENOMEM; + + rtw89_hex_dump(rtwdev, RTW89_DBG_ACPI, "policy_tas: ", *policy, + sizeof(*ptr)); + return 0; +} + int rtw89_acpi_evaluate_dsm(struct rtw89_dev *rtwdev, enum rtw89_acpi_dsm_func func, struct rtw89_acpi_dsm_result *res) @@ -142,6 +300,8 @@ int rtw89_acpi_evaluate_dsm(struct rtw89_dev *rtwdev, else if (func == RTW89_ACPI_DSM_FUNC_6GHZ_SP_SUP) ret = rtw89_acpi_dsm_get_policy_6ghz_sp(rtwdev, obj, &res->u.policy_6ghz_sp); + else if (func == RTW89_ACPI_DSM_FUNC_TAS_EN) + ret = rtw89_acpi_dsm_get_policy_tas(rtwdev, obj, &res->u.policy_tas); else ret = rtw89_acpi_dsm_get_value(rtwdev, obj, &res->u.value); @@ -152,46 +312,875 @@ int rtw89_acpi_evaluate_dsm(struct rtw89_dev *rtwdev, int rtw89_acpi_evaluate_rtag(struct rtw89_dev *rtwdev, struct rtw89_acpi_rtag_result *res) { - struct acpi_buffer buf = {ACPI_ALLOCATE_BUFFER, NULL}; - acpi_handle root, handle; - union acpi_object *obj; - acpi_status status; + const struct rtw89_acpi_data *data; u32 buf_len; int ret = 0; - root = ACPI_HANDLE(rtwdev->dev); - if (!root) - return -EOPNOTSUPP; - - status = acpi_get_handle(root, (acpi_string)"RTAG", &handle); - if (ACPI_FAILURE(status)) + data = rtw89_acpi_evaluate_method(rtwdev, "RTAG"); + if (!data) return -EIO; - status = acpi_evaluate_object(handle, NULL, NULL, &buf); - if (ACPI_FAILURE(status)) - return -EIO; + buf_len = data->len; + if (buf_len != sizeof(*res)) { + rtw89_debug(rtwdev, RTW89_DBG_ACPI, "%s: invalid buffer length: %u\n", + __func__, buf_len); + ret = -EINVAL; + goto out; + } - obj = buf.pointer; - if (obj->type != ACPI_TYPE_BUFFER) { + *res = *(struct rtw89_acpi_rtag_result *)data->buf; + + rtw89_hex_dump(rtwdev, RTW89_DBG_ACPI, "antenna_gain: ", res, sizeof(*res)); + +out: + kfree(data); + return ret; +} + +enum rtw89_acpi_sar_subband rtw89_acpi_sar_get_subband(struct rtw89_dev *rtwdev, + u32 center_freq) +{ + switch (center_freq) { + default: rtw89_debug(rtwdev, RTW89_DBG_ACPI, - "acpi: expect buffer but type: %d\n", obj->type); - ret = -EINVAL; + "center freq %u to ACPI SAR subband is unhandled\n", + center_freq); + fallthrough; + case 2412 ... 2484: + return RTW89_ACPI_SAR_2GHZ_SUBBAND; + case 5180 ... 5240: + return RTW89_ACPI_SAR_5GHZ_SUBBAND_1; + case 5250 ... 5320: + return RTW89_ACPI_SAR_5GHZ_SUBBAND_2; + case 5500 ... 5720: + return RTW89_ACPI_SAR_5GHZ_SUBBAND_2E; + case 5745 ... 5885: + return RTW89_ACPI_SAR_5GHZ_SUBBAND_3_4; + case 5955 ... 6155: + return RTW89_ACPI_SAR_6GHZ_SUBBAND_5_L; + case 6175 ... 6415: + return RTW89_ACPI_SAR_6GHZ_SUBBAND_5_H; + case 6435 ... 6515: + return RTW89_ACPI_SAR_6GHZ_SUBBAND_6; + case 6535 ... 6695: + return RTW89_ACPI_SAR_6GHZ_SUBBAND_7_L; + case 6715 ... 6855: + return RTW89_ACPI_SAR_6GHZ_SUBBAND_7_H; + + /* freq 6875 (ch 185, 20MHz) spans RTW89_ACPI_SAR_6GHZ_SUBBAND_7_H + * and RTW89_ACPI_SAR_6GHZ_SUBBAND_8, so directly describe it with + * struct rtw89_6ghz_span. + */ + + case 6895 ... 7115: + return RTW89_ACPI_SAR_6GHZ_SUBBAND_8; + } +} + +enum rtw89_band rtw89_acpi_sar_subband_to_band(struct rtw89_dev *rtwdev, + enum rtw89_acpi_sar_subband subband) +{ + switch (subband) { + default: + rtw89_debug(rtwdev, RTW89_DBG_ACPI, + "ACPI SAR subband %u to band is unhandled\n", subband); + fallthrough; + case RTW89_ACPI_SAR_2GHZ_SUBBAND: + return RTW89_BAND_2G; + case RTW89_ACPI_SAR_5GHZ_SUBBAND_1: + return RTW89_BAND_5G; + case RTW89_ACPI_SAR_5GHZ_SUBBAND_2: + return RTW89_BAND_5G; + case RTW89_ACPI_SAR_5GHZ_SUBBAND_2E: + return RTW89_BAND_5G; + case RTW89_ACPI_SAR_5GHZ_SUBBAND_3_4: + return RTW89_BAND_5G; + case RTW89_ACPI_SAR_6GHZ_SUBBAND_5_L: + return RTW89_BAND_6G; + case RTW89_ACPI_SAR_6GHZ_SUBBAND_5_H: + return RTW89_BAND_6G; + case RTW89_ACPI_SAR_6GHZ_SUBBAND_6: + return RTW89_BAND_6G; + case RTW89_ACPI_SAR_6GHZ_SUBBAND_7_L: + return RTW89_BAND_6G; + case RTW89_ACPI_SAR_6GHZ_SUBBAND_7_H: + return RTW89_BAND_6G; + case RTW89_ACPI_SAR_6GHZ_SUBBAND_8: + return RTW89_BAND_6G; + } +} + +static u8 rtw89_acpi_sar_rfpath_to_hp_antidx(enum rtw89_rf_path rfpath) +{ + switch (rfpath) { + default: + case RF_PATH_B: + return 0; + case RF_PATH_A: + return 1; + } +} + +static u8 rtw89_acpi_sar_rfpath_to_rt_antidx(enum rtw89_rf_path rfpath) +{ + switch (rfpath) { + default: + case RF_PATH_A: + return 0; + case RF_PATH_B: + return 1; + } +} + +static s16 rtw89_acpi_sar_normalize_hp_val(u8 v) +{ + static const u8 bias = 10; + static const u8 fct = 1; + u16 res; + + BUILD_BUG_ON(fct > TXPWR_FACTOR_OF_RTW89_ACPI_SAR); + + res = (bias << TXPWR_FACTOR_OF_RTW89_ACPI_SAR) + + (v << (TXPWR_FACTOR_OF_RTW89_ACPI_SAR - fct)); + + return min_t(s32, res, MAX_VAL_OF_RTW89_ACPI_SAR); +} + +static s16 rtw89_acpi_sar_normalize_rt_val(u8 v) +{ + static const u8 fct = 3; + u16 res; + + BUILD_BUG_ON(fct > TXPWR_FACTOR_OF_RTW89_ACPI_SAR); + + res = v << (TXPWR_FACTOR_OF_RTW89_ACPI_SAR - fct); + + return min_t(s32, res, MAX_VAL_OF_RTW89_ACPI_SAR); +} + +static +void rtw89_acpi_sar_load_std_legacy(struct rtw89_dev *rtwdev, + const struct rtw89_acpi_sar_recognition *rec, + const void *content, + struct rtw89_sar_entry_from_acpi *ent) +{ + const struct rtw89_acpi_sar_std_legacy *ptr = content; + enum rtw89_acpi_sar_subband subband; + enum rtw89_rf_path path; + + for (subband = 0; subband < NUM_OF_RTW89_ACPI_SAR_SUBBAND; subband++) { + for (path = 0; path < NUM_OF_RTW89_ACPI_SAR_RF_PATH; path++) { + u8 antidx = rec->rfpath_to_antidx(path); + + if (subband < RTW89_ACPI_SAR_SUBBAND_NR_LEGACY) + ent->v[subband][path] = + rec->normalize(ptr->v[antidx][subband]); + else + ent->v[subband][path] = MAX_VAL_OF_RTW89_ACPI_SAR; + } + } +} + +static +void rtw89_acpi_sar_load_std_has_6ghz(struct rtw89_dev *rtwdev, + const struct rtw89_acpi_sar_recognition *rec, + const void *content, + struct rtw89_sar_entry_from_acpi *ent) +{ + const struct rtw89_acpi_sar_std_has_6ghz *ptr = content; + enum rtw89_acpi_sar_subband subband; + enum rtw89_rf_path path; + + BUILD_BUG_ON(RTW89_ACPI_SAR_SUBBAND_NR_HAS_6GHZ != NUM_OF_RTW89_ACPI_SAR_SUBBAND); + + for (subband = 0; subband < NUM_OF_RTW89_ACPI_SAR_SUBBAND; subband++) { + for (path = 0; path < NUM_OF_RTW89_ACPI_SAR_RF_PATH; path++) { + u8 antidx = rec->rfpath_to_antidx(path); + + ent->v[subband][path] = rec->normalize(ptr->v[antidx][subband]); + } + } +} + +static +void rtw89_acpi_sar_load_sml_legacy(struct rtw89_dev *rtwdev, + const struct rtw89_acpi_sar_recognition *rec, + const void *content, + struct rtw89_sar_entry_from_acpi *ent) +{ + const struct rtw89_acpi_sar_sml_legacy *ptr = content; + enum rtw89_acpi_sar_subband subband; + enum rtw89_rf_path path; + + for (subband = 0; subband < NUM_OF_RTW89_ACPI_SAR_SUBBAND; subband++) { + for (path = 0; path < NUM_OF_RTW89_ACPI_SAR_RF_PATH; path++) { + u8 antidx = rec->rfpath_to_antidx(path); + + if (subband < RTW89_ACPI_SAR_SUBBAND_NR_LEGACY) + ent->v[subband][path] = + rec->normalize(ptr->v[antidx][subband]); + else + ent->v[subband][path] = MAX_VAL_OF_RTW89_ACPI_SAR; + } + } +} + +static +void rtw89_acpi_sar_load_sml_has_6ghz(struct rtw89_dev *rtwdev, + const struct rtw89_acpi_sar_recognition *rec, + const void *content, + struct rtw89_sar_entry_from_acpi *ent) +{ + const struct rtw89_acpi_sar_sml_has_6ghz *ptr = content; + enum rtw89_acpi_sar_subband subband; + enum rtw89_rf_path path; + + BUILD_BUG_ON(RTW89_ACPI_SAR_SUBBAND_NR_HAS_6GHZ != NUM_OF_RTW89_ACPI_SAR_SUBBAND); + + for (subband = 0; subband < NUM_OF_RTW89_ACPI_SAR_SUBBAND; subband++) { + for (path = 0; path < NUM_OF_RTW89_ACPI_SAR_RF_PATH; path++) { + u8 antidx = rec->rfpath_to_antidx(path); + + ent->v[subband][path] = rec->normalize(ptr->v[antidx][subband]); + } + } +} + +static s16 rtw89_acpi_geo_sar_normalize_delta(s8 delta) +{ + static const u8 fct = 1; + + BUILD_BUG_ON(fct > TXPWR_FACTOR_OF_RTW89_ACPI_SAR); + + return delta << (TXPWR_FACTOR_OF_RTW89_ACPI_SAR - fct); +} + +static enum rtw89_acpi_geo_sar_regd_hp +rtw89_acpi_geo_sar_regd_convert_hp_idx(enum rtw89_regulation_type regd) +{ + switch (regd) { + case RTW89_FCC: + case RTW89_IC: + case RTW89_NCC: + case RTW89_CHILE: + case RTW89_MEXICO: + return RTW89_ACPI_GEO_SAR_REGD_HP_FCC; + case RTW89_ETSI: + case RTW89_MKK: + case RTW89_ACMA: + return RTW89_ACPI_GEO_SAR_REGD_HP_ETSI; + default: + case RTW89_WW: + case RTW89_NA: + case RTW89_KCC: + return RTW89_ACPI_GEO_SAR_REGD_HP_WW; + } +} + +static enum rtw89_acpi_geo_sar_regd_rt +rtw89_acpi_geo_sar_regd_convert_rt_idx(enum rtw89_regulation_type regd) +{ + switch (regd) { + case RTW89_FCC: + case RTW89_NCC: + case RTW89_CHILE: + case RTW89_MEXICO: + return RTW89_ACPI_GEO_SAR_REGD_RT_FCC; + case RTW89_ETSI: + case RTW89_ACMA: + return RTW89_ACPI_GEO_SAR_REGD_RT_ETSI; + case RTW89_MKK: + return RTW89_ACPI_GEO_SAR_REGD_RT_MKK; + case RTW89_IC: + return RTW89_ACPI_GEO_SAR_REGD_RT_IC; + case RTW89_KCC: + return RTW89_ACPI_GEO_SAR_REGD_RT_KCC; + default: + case RTW89_WW: + case RTW89_NA: + return RTW89_ACPI_GEO_SAR_REGD_RT_WW; + } +} + +static +void rtw89_acpi_geo_sar_load_by_hp(struct rtw89_dev *rtwdev, + const struct rtw89_acpi_geo_sar_hp_val *ptr, + enum rtw89_rf_path path, s16 *val) +{ + u8 antidx = rtw89_acpi_sar_rfpath_to_hp_antidx(path); + s16 delta = rtw89_acpi_geo_sar_normalize_delta(ptr->delta[antidx]); + s16 max = rtw89_acpi_sar_normalize_hp_val(ptr->max); + + *val = clamp_t(s32, (*val) + delta, MIN_VAL_OF_RTW89_ACPI_SAR, max); +} + +static +void rtw89_acpi_geo_sar_load_by_rt(struct rtw89_dev *rtwdev, + const struct rtw89_acpi_geo_sar_rt_val *ptr, + s16 *val) +{ + s16 delta = rtw89_acpi_geo_sar_normalize_delta(ptr->delta); + s16 max = rtw89_acpi_sar_normalize_rt_val(ptr->max); + + *val = clamp_t(s32, (*val) + delta, MIN_VAL_OF_RTW89_ACPI_SAR, max); +} + +static +void rtw89_acpi_geo_sar_load_hp_legacy(struct rtw89_dev *rtwdev, + const void *content, + enum rtw89_regulation_type regd, + struct rtw89_sar_entry_from_acpi *ent) +{ + const struct rtw89_acpi_geo_sar_hp_legacy *ptr = content; + const struct rtw89_acpi_geo_sar_hp_legacy_entry *ptr_ent; + const struct rtw89_acpi_geo_sar_hp_val *ptr_ent_val; + enum rtw89_acpi_geo_sar_regd_hp geo_idx = + rtw89_acpi_geo_sar_regd_convert_hp_idx(regd); + enum rtw89_acpi_sar_subband subband; + enum rtw89_rf_path path; + enum rtw89_band band; + + ptr_ent = &ptr->entries[geo_idx]; + + for (subband = 0; subband < NUM_OF_RTW89_ACPI_SAR_SUBBAND; subband++) { + band = rtw89_acpi_sar_subband_to_band(rtwdev, subband); + switch (band) { + case RTW89_BAND_2G: + ptr_ent_val = &ptr_ent->val_2ghz; + break; + case RTW89_BAND_5G: + ptr_ent_val = &ptr_ent->val_5ghz; + break; + default: + case RTW89_BAND_6G: + ptr_ent_val = NULL; + break; + } + + if (!ptr_ent_val) + continue; + + for (path = 0; path < NUM_OF_RTW89_ACPI_SAR_RF_PATH; path++) + rtw89_acpi_geo_sar_load_by_hp(rtwdev, ptr_ent_val, path, + &ent->v[subband][path]); + } +} + +static +void rtw89_acpi_geo_sar_load_hp_has_6ghz(struct rtw89_dev *rtwdev, + const void *content, + enum rtw89_regulation_type regd, + struct rtw89_sar_entry_from_acpi *ent) +{ + const struct rtw89_acpi_geo_sar_hp_has_6ghz *ptr = content; + const struct rtw89_acpi_geo_sar_hp_has_6ghz_entry *ptr_ent; + const struct rtw89_acpi_geo_sar_hp_val *ptr_ent_val; + enum rtw89_acpi_geo_sar_regd_hp geo_idx = + rtw89_acpi_geo_sar_regd_convert_hp_idx(regd); + enum rtw89_acpi_sar_subband subband; + enum rtw89_rf_path path; + enum rtw89_band band; + + ptr_ent = &ptr->entries[geo_idx]; + + for (subband = 0; subband < NUM_OF_RTW89_ACPI_SAR_SUBBAND; subband++) { + band = rtw89_acpi_sar_subband_to_band(rtwdev, subband); + switch (band) { + case RTW89_BAND_2G: + ptr_ent_val = &ptr_ent->val_2ghz; + break; + case RTW89_BAND_5G: + ptr_ent_val = &ptr_ent->val_5ghz; + break; + case RTW89_BAND_6G: + ptr_ent_val = &ptr_ent->val_6ghz; + break; + default: + ptr_ent_val = NULL; + break; + } + + if (!ptr_ent_val) + continue; + + for (path = 0; path < NUM_OF_RTW89_ACPI_SAR_RF_PATH; path++) + rtw89_acpi_geo_sar_load_by_hp(rtwdev, ptr_ent_val, path, + &ent->v[subband][path]); + } +} + +static +void rtw89_acpi_geo_sar_load_rt_legacy(struct rtw89_dev *rtwdev, + const void *content, + enum rtw89_regulation_type regd, + struct rtw89_sar_entry_from_acpi *ent) +{ + const struct rtw89_acpi_geo_sar_rt_legacy *ptr = content; + const struct rtw89_acpi_geo_sar_rt_legacy_entry *ptr_ent; + const struct rtw89_acpi_geo_sar_rt_val *ptr_ent_val; + enum rtw89_acpi_geo_sar_regd_rt geo_idx = + rtw89_acpi_geo_sar_regd_convert_rt_idx(regd); + enum rtw89_acpi_sar_subband subband; + enum rtw89_rf_path path; + enum rtw89_band band; + + ptr_ent = &ptr->entries[geo_idx]; + + for (subband = 0; subband < NUM_OF_RTW89_ACPI_SAR_SUBBAND; subband++) { + band = rtw89_acpi_sar_subband_to_band(rtwdev, subband); + switch (band) { + case RTW89_BAND_2G: + ptr_ent_val = &ptr_ent->val_2ghz; + break; + case RTW89_BAND_5G: + ptr_ent_val = &ptr_ent->val_5ghz; + break; + default: + case RTW89_BAND_6G: + ptr_ent_val = NULL; + break; + } + + if (!ptr_ent_val) + continue; + + for (path = 0; path < NUM_OF_RTW89_ACPI_SAR_RF_PATH; path++) + rtw89_acpi_geo_sar_load_by_rt(rtwdev, ptr_ent_val, + &ent->v[subband][path]); + } +} + +static +void rtw89_acpi_geo_sar_load_rt_has_6ghz(struct rtw89_dev *rtwdev, + const void *content, + enum rtw89_regulation_type regd, + struct rtw89_sar_entry_from_acpi *ent) +{ + const struct rtw89_acpi_geo_sar_rt_has_6ghz *ptr = content; + const struct rtw89_acpi_geo_sar_rt_has_6ghz_entry *ptr_ent; + const struct rtw89_acpi_geo_sar_rt_val *ptr_ent_val; + enum rtw89_acpi_geo_sar_regd_rt geo_idx = + rtw89_acpi_geo_sar_regd_convert_rt_idx(regd); + enum rtw89_acpi_sar_subband subband; + enum rtw89_rf_path path; + enum rtw89_band band; + + ptr_ent = &ptr->entries[geo_idx]; + + for (subband = 0; subband < NUM_OF_RTW89_ACPI_SAR_SUBBAND; subband++) { + band = rtw89_acpi_sar_subband_to_band(rtwdev, subband); + switch (band) { + case RTW89_BAND_2G: + ptr_ent_val = &ptr_ent->val_2ghz; + break; + case RTW89_BAND_5G: + ptr_ent_val = &ptr_ent->val_5ghz; + break; + case RTW89_BAND_6G: + ptr_ent_val = &ptr_ent->val_6ghz; + break; + default: + ptr_ent_val = NULL; + break; + } + + if (!ptr_ent_val) + continue; + + for (path = 0; path < NUM_OF_RTW89_ACPI_SAR_RF_PATH; path++) + rtw89_acpi_geo_sar_load_by_rt(rtwdev, ptr_ent_val, + &ent->v[subband][path]); + } +} + +#define RTW89_ACPI_GEO_SAR_DECL_HANDLER(type) \ +static const struct rtw89_acpi_geo_sar_handler \ +rtw89_acpi_geo_sar_handler_ ## type = { \ + .data_size = RTW89_ACPI_GEO_SAR_SIZE_OF(type), \ + .load = rtw89_acpi_geo_sar_load_ ## type, \ +} + +RTW89_ACPI_GEO_SAR_DECL_HANDLER(hp_legacy); +RTW89_ACPI_GEO_SAR_DECL_HANDLER(hp_has_6ghz); +RTW89_ACPI_GEO_SAR_DECL_HANDLER(rt_legacy); +RTW89_ACPI_GEO_SAR_DECL_HANDLER(rt_has_6ghz); + +static const struct rtw89_acpi_sar_recognition rtw89_acpi_sar_recs[] = { + { + .id = { + .cid = RTW89_ACPI_SAR_CID_HP, + .rev = RTW89_ACPI_SAR_REV_LEGACY, + .size = RTW89_ACPI_SAR_SIZE_OF(std_legacy), + }, + .geo = &rtw89_acpi_geo_sar_handler_hp_legacy, + + .rfpath_to_antidx = rtw89_acpi_sar_rfpath_to_hp_antidx, + .normalize = rtw89_acpi_sar_normalize_hp_val, + .load = rtw89_acpi_sar_load_std_legacy, + }, + { + .id = { + .cid = RTW89_ACPI_SAR_CID_HP, + .rev = RTW89_ACPI_SAR_REV_HAS_6GHZ, + .size = RTW89_ACPI_SAR_SIZE_OF(std_has_6ghz), + }, + .geo = &rtw89_acpi_geo_sar_handler_hp_has_6ghz, + + .rfpath_to_antidx = rtw89_acpi_sar_rfpath_to_hp_antidx, + .normalize = rtw89_acpi_sar_normalize_hp_val, + .load = rtw89_acpi_sar_load_std_has_6ghz, + }, + { + .id = { + .cid = RTW89_ACPI_SAR_CID_RT, + .rev = RTW89_ACPI_SAR_REV_LEGACY, + .size = RTW89_ACPI_SAR_SIZE_OF(std_legacy), + }, + .geo = &rtw89_acpi_geo_sar_handler_rt_legacy, + + .rfpath_to_antidx = rtw89_acpi_sar_rfpath_to_rt_antidx, + .normalize = rtw89_acpi_sar_normalize_rt_val, + .load = rtw89_acpi_sar_load_std_legacy, + }, + { + .id = { + .cid = RTW89_ACPI_SAR_CID_RT, + .rev = RTW89_ACPI_SAR_REV_HAS_6GHZ, + .size = RTW89_ACPI_SAR_SIZE_OF(std_has_6ghz), + }, + .geo = &rtw89_acpi_geo_sar_handler_rt_has_6ghz, + + .rfpath_to_antidx = rtw89_acpi_sar_rfpath_to_rt_antidx, + .normalize = rtw89_acpi_sar_normalize_rt_val, + .load = rtw89_acpi_sar_load_std_has_6ghz, + }, + { + .id = { + .cid = RTW89_ACPI_SAR_CID_RT, + .rev = RTW89_ACPI_SAR_REV_LEGACY, + .size = RTW89_ACPI_SAR_SIZE_OF(sml_legacy), + }, + .geo = &rtw89_acpi_geo_sar_handler_rt_legacy, + + .rfpath_to_antidx = rtw89_acpi_sar_rfpath_to_rt_antidx, + .normalize = rtw89_acpi_sar_normalize_rt_val, + .load = rtw89_acpi_sar_load_sml_legacy, + }, + { + .id = { + .cid = RTW89_ACPI_SAR_CID_RT, + .rev = RTW89_ACPI_SAR_REV_HAS_6GHZ, + .size = RTW89_ACPI_SAR_SIZE_OF(sml_has_6ghz), + }, + .geo = &rtw89_acpi_geo_sar_handler_rt_has_6ghz, + + .rfpath_to_antidx = rtw89_acpi_sar_rfpath_to_rt_antidx, + .normalize = rtw89_acpi_sar_normalize_rt_val, + .load = rtw89_acpi_sar_load_sml_has_6ghz, + }, +}; + +struct rtw89_acpi_sar_rec_parm { + u32 pld_len; + u8 tbl_cnt; + u16 cid; + u8 rev; +}; + +static const struct rtw89_acpi_sar_recognition * +rtw89_acpi_sar_recognize(struct rtw89_dev *rtwdev, + const struct rtw89_acpi_sar_rec_parm *parm) +{ + const u32 tbl_len = parm->pld_len / parm->tbl_cnt; + const struct rtw89_acpi_sar_recognition *rec; + struct rtw89_acpi_sar_identifier id = {}; + + rtw89_debug(rtwdev, RTW89_DBG_ACPI, + "%s: cid %u, rev %u, tbl len %u, tbl cnt %u\n", + __func__, parm->cid, parm->rev, tbl_len, parm->tbl_cnt); + + if (unlikely(parm->pld_len % parm->tbl_cnt)) { + rtw89_debug(rtwdev, RTW89_DBG_ACPI, "invalid pld len %u\n", + parm->pld_len); + return NULL; + } + + if (unlikely(tbl_len > RTW89_ACPI_SAR_SIZE_MAX)) { + rtw89_debug(rtwdev, RTW89_DBG_ACPI, "invalid tbl len %u\n", + tbl_len); + return NULL; + } + + if (unlikely(parm->tbl_cnt > MAX_NUM_OF_RTW89_ACPI_SAR_TBL)) { + rtw89_debug(rtwdev, RTW89_DBG_ACPI, "invalid tbl cnt %u\n", + parm->tbl_cnt); + return NULL; + } + + switch (parm->cid) { + case RTW89_ACPI_SAR_CID_HP: + case RTW89_ACPI_SAR_CID_RT: + id.cid = parm->cid; + break; + default: + rtw89_debug(rtwdev, RTW89_DBG_ACPI, "invalid cid 0x%x\n", + parm->cid); + return NULL; + } + + switch (parm->rev) { + case RTW89_ACPI_SAR_REV_LEGACY: + case RTW89_ACPI_SAR_REV_HAS_6GHZ: + id.rev = parm->rev; + break; + default: + rtw89_debug(rtwdev, RTW89_DBG_ACPI, "invalid rev %u\n", + parm->rev); + return NULL; + } + + id.size = tbl_len; + for (unsigned int i = 0; i < ARRAY_SIZE(rtw89_acpi_sar_recs); i++) { + rec = &rtw89_acpi_sar_recs[i]; + if (memcmp(&rec->id, &id, sizeof(rec->id)) == 0) + return rec; + } + + rtw89_debug(rtwdev, RTW89_DBG_ACPI, "failed to recognize\n"); + return NULL; +} + +static const struct rtw89_acpi_sar_recognition * +rtw89_acpi_evaluate_static_sar(struct rtw89_dev *rtwdev, + struct rtw89_sar_cfg_acpi *cfg) +{ + const struct rtw89_acpi_sar_recognition *rec = NULL; + const struct rtw89_acpi_static_sar_hdr *hdr; + struct rtw89_sar_entry_from_acpi tmp = {}; + struct rtw89_acpi_sar_rec_parm parm = {}; + struct rtw89_sar_table_from_acpi *tbl; + const struct rtw89_acpi_data *data; + u32 len; + + data = rtw89_acpi_evaluate_method(rtwdev, RTW89_ACPI_METHOD_STATIC_SAR); + if (!data) + return NULL; + + rtw89_debug(rtwdev, RTW89_DBG_ACPI, "acpi load static sar\n"); + + len = data->len; + if (len <= sizeof(*hdr)) { + rtw89_debug(rtwdev, RTW89_DBG_ACPI, "invalid buf len %u\n", len); goto out; } - buf_len = obj->buffer.length; - if (buf_len != sizeof(*res)) { - rtw89_debug(rtwdev, RTW89_DBG_ACPI, "%s: invalid buffer length: %u\n", - __func__, buf_len); + hdr = (typeof(hdr))data->buf; + + parm.cid = le16_to_cpu(hdr->cid); + parm.rev = hdr->rev; + parm.tbl_cnt = 1; + parm.pld_len = len - sizeof(*hdr); + + rec = rtw89_acpi_sar_recognize(rtwdev, &parm); + if (!rec) + goto out; + + rec->load(rtwdev, rec, hdr->content, &tmp); + + tbl = &cfg->tables[0]; + for (u8 regd = 0; regd < RTW89_REGD_NUM; regd++) + tbl->entries[regd] = tmp; + + cfg->valid_num = 1; + +out: + kfree(data); + return rec; +} + +static const struct rtw89_acpi_sar_recognition * +rtw89_acpi_evaluate_dynamic_sar(struct rtw89_dev *rtwdev, + struct rtw89_sar_cfg_acpi *cfg) +{ + const struct rtw89_acpi_sar_recognition *rec = NULL; + const struct rtw89_acpi_dynamic_sar_hdr *hdr; + struct rtw89_acpi_sar_rec_parm parm = {}; + struct rtw89_sar_table_from_acpi *tbl; + const struct rtw89_acpi_data *data; + u32 len; + + data = rtw89_acpi_evaluate_method(rtwdev, RTW89_ACPI_METHOD_DYNAMIC_SAR); + if (!data) + return NULL; + + rtw89_debug(rtwdev, RTW89_DBG_ACPI, "acpi load dynamic sar\n"); + + len = data->len; + if (len <= sizeof(*hdr)) { + rtw89_debug(rtwdev, RTW89_DBG_ACPI, "invalid buf len %u\n", len); + goto out; + } + + hdr = (typeof(hdr))data->buf; + + parm.cid = le16_to_cpu(hdr->cid); + parm.rev = hdr->rev; + parm.tbl_cnt = hdr->cnt; + parm.pld_len = len - sizeof(*hdr); + + rec = rtw89_acpi_sar_recognize(rtwdev, &parm); + if (!rec) + goto out; + + for (unsigned int i = 0; i < hdr->cnt; i++) { + const u8 *content = hdr->content + rec->id.size * i; + struct rtw89_sar_entry_from_acpi tmp = {}; + + rec->load(rtwdev, rec, content, &tmp); + + tbl = &cfg->tables[i]; + for (u8 regd = 0; regd < RTW89_REGD_NUM; regd++) + tbl->entries[regd] = tmp; + } + + cfg->valid_num = hdr->cnt; + +out: + kfree(data); + return rec; +} + +int rtw89_acpi_evaluate_dynamic_sar_indicator(struct rtw89_dev *rtwdev, + struct rtw89_sar_cfg_acpi *cfg, + bool *poll_changed) +{ + struct rtw89_sar_indicator_from_acpi *ind = &cfg->indicator; + struct rtw89_sar_indicator_from_acpi tmp = *ind; + const struct rtw89_acpi_data *data; + const u8 *tbl_base1_by_ant; + enum rtw89_rf_path path; + int ret = 0; + u32 len; + + data = rtw89_acpi_evaluate_method(rtwdev, RTW89_ACPI_METHOD_DYNAMIC_SAR_INDICATOR); + if (!data) + return -EFAULT; + + if (!poll_changed) + rtw89_debug(rtwdev, RTW89_DBG_ACPI, "acpi load dynamic sar indicator\n"); + + len = data->len; + if (len != ind->fields) { + rtw89_debug(rtwdev, RTW89_DBG_ACPI, "invalid buf len %u\n", len); ret = -EINVAL; goto out; } - *res = *(struct rtw89_acpi_rtag_result *)obj->buffer.pointer; + tbl_base1_by_ant = data->buf; - rtw89_hex_dump(rtwdev, RTW89_DBG_ACPI, "antenna_gain: ", res, sizeof(*res)); + for (path = 0; path < NUM_OF_RTW89_ACPI_SAR_RF_PATH; path++) { + u8 antidx = ind->rfpath_to_antidx(path); + u8 sel; + + if (antidx >= ind->fields) + antidx = 0; + + /* convert the table index from 1-based to 0-based */ + sel = tbl_base1_by_ant[antidx] - 1; + if (sel >= cfg->valid_num) + sel = 0; + + tmp.tblsel[path] = sel; + } + + if (memcmp(ind, &tmp, sizeof(*ind)) == 0) { + if (poll_changed) + *poll_changed = false; + } else { + if (poll_changed) + *poll_changed = true; + + *ind = tmp; + } out: - ACPI_FREE(obj); + kfree(data); return ret; } + +static +void rtw89_acpi_evaluate_geo_sar(struct rtw89_dev *rtwdev, + const struct rtw89_acpi_geo_sar_handler *hdl, + struct rtw89_sar_cfg_acpi *cfg) +{ + const struct rtw89_acpi_data *data; + u32 len; + + data = rtw89_acpi_evaluate_method(rtwdev, RTW89_ACPI_METHOD_GEO_SAR); + if (!data) + return; + + rtw89_debug(rtwdev, RTW89_DBG_ACPI, "acpi load geo sar\n"); + + len = data->len; + if (len != hdl->data_size) { + rtw89_debug(rtwdev, RTW89_DBG_ACPI, "invalid buf len %u (expected %u)\n", + len, hdl->data_size); + goto out; + } + + for (unsigned int i = 0; i < cfg->valid_num; i++) + for (u8 regd = 0; regd < RTW89_REGD_NUM; regd++) + hdl->load(rtwdev, data->buf, regd, &cfg->tables[i].entries[regd]); + +out: + kfree(data); +} + +int rtw89_acpi_evaluate_sar(struct rtw89_dev *rtwdev, + struct rtw89_sar_cfg_acpi *cfg) +{ + struct rtw89_sar_indicator_from_acpi *ind = &cfg->indicator; + const struct rtw89_acpi_sar_recognition *rec; + bool fetch_indicator = false; + int ret; + + rec = rtw89_acpi_evaluate_static_sar(rtwdev, cfg); + if (rec) + goto recognized; + + rec = rtw89_acpi_evaluate_dynamic_sar(rtwdev, cfg); + if (!rec) + return -ENOENT; + + fetch_indicator = true; + +recognized: + rtw89_acpi_evaluate_geo_sar(rtwdev, rec->geo, cfg); + + switch (rec->id.cid) { + case RTW89_ACPI_SAR_CID_HP: + cfg->downgrade_2tx = 3 << TXPWR_FACTOR_OF_RTW89_ACPI_SAR; + ind->fields = RTW89_ACPI_SAR_ANT_NR_STD; + break; + case RTW89_ACPI_SAR_CID_RT: + cfg->downgrade_2tx = 0; + ind->fields = 1; + break; + default: + return -EFAULT; + } + + if (fetch_indicator) { + ind->rfpath_to_antidx = rec->rfpath_to_antidx; + ret = rtw89_acpi_evaluate_dynamic_sar_indicator(rtwdev, cfg, NULL); + if (ret) + fetch_indicator = false; + } + + if (!fetch_indicator) + memset(ind->tblsel, 0, sizeof(ind->tblsel)); + + ind->enable_sync = fetch_indicator; + return 0; +} |