@@ -1449,6 +1449,8 @@ void rtw_core_deinit(struct rtw_dev *rtwdev)
kfree(rsvd_pkt);
}
+ rtw_sar_release_table(rtwdev);
+
mutex_destroy(&rtwdev->mutex);
mutex_destroy(&rtwdev->coex.mutex);
mutex_destroy(&rtwdev->hal.tx_power_mutex);
@@ -46,6 +46,10 @@ extern struct rtw_chip_info rtw8822c_hw_spec;
#define RTW_MAX_CHANNEL_NUM_5G 49
struct rtw_dev;
+struct rtw_sar_rwrd;
+union rtw_sar_rwsi;
+union rtw_sar_rwgs;
+struct rtw_sar_read;
enum rtw_hci_type {
RTW_HCI_TYPE_PCIE,
@@ -1522,6 +1526,10 @@ struct rtw_fw_state {
struct rtw_sar {
enum rtw_sar_sources source;
+ struct rtw_sar_rwrd *rwrd;
+ union rtw_sar_rwsi *rwsi;
+ union rtw_sar_rwgs *rwgs;
+ const struct rtw_sar_read *read;
};
struct rtw_hal {
@@ -187,14 +187,315 @@ static int rtw_sar_load_static_tables(struct rtw_dev *rtwdev)
return 0;
}
+
+#define ACPI_RWRD_METHOD "RWRD"
+#define ACPI_RWSI_METHOD "RWSI"
+#define ACPI_RWGS_METHOD "RWGS"
+
+#define RTW_SAR_RWRD_ID_HP 0x5048
+#define RTW_SAR_RWRD_ID_RT 0x5452
+
+#define RTW_SAR_RWRD_CHAIN_NR 4
+
+struct rtw_sar_rwrd {
+ u16 id;
+ u8 en;
+ u8 count;
+ struct {
+ struct rtw_sar_limits chain[RTW_SAR_RWRD_CHAIN_NR];
+ } mode[0];
+} __packed;
+
+struct rtw_sar_rwsi_hp {
+ u8 index[RTW_SAR_RWRD_CHAIN_NR];
+} __packed;
+
+struct rtw_sar_rwsi_rt {
+ u8 index;
+} __packed;
+
+union rtw_sar_rwsi {
+ struct rtw_sar_rwsi_hp hp;
+ struct rtw_sar_rwsi_rt rt;
+};
+
+enum rtw_sar_rwgs_band {
+ RTW_SAR_RWGS_2G,
+ RTW_SAR_RWGS_5G,
+ RTW_SAR_RWGS_BAND_NR,
+};
+
+enum rtw_sar_rwgs_geo_hp {
+ RTW_SAR_RWGS_HP_FCC_IC,
+ RTW_SAR_RWGS_HP_ETSI_MKK,
+ RTW_SAR_RWGS_HP_WW_KCC,
+
+ RTW_SAR_RWGS_HP_NR,
+};
+
+struct rtw_sar_rwgs_hp {
+ struct {
+ struct {
+ s8 max; /* Q1 + 10 */
+ s8 delta[4]; /* Q1 */
+ } band[RTW_SAR_RWGS_BAND_NR];
+ } geo[RTW_SAR_RWGS_HP_NR];
+} __packed;
+
+enum rtw_sar_rwgs_geo_rt {
+ RTW_SAR_RWGS_RT_FCC,
+ RTW_SAR_RWGS_RT_CE,
+ RTW_SAR_RWGS_RT_MKK,
+ RTW_SAR_RWGS_RT_IC,
+ RTW_SAR_RWGS_RT_KCC,
+ RTW_SAR_RWGS_RT_WW,
+
+ RTW_SAR_RWGS_RT_NR,
+};
+
+struct rtw_sar_rwgs_rt {
+ struct {
+ struct {
+ u8 max; /* Q3 */
+ s8 delta; /* Q1 */
+ } band[RTW_SAR_RWGS_BAND_NR];
+ } geo[RTW_SAR_RWGS_RT_NR];
+} __packed;
+
+union rtw_sar_rwgs {
+ struct rtw_sar_rwgs_hp hp;
+ struct rtw_sar_rwgs_rt rt;
+};
+
+struct rtw_sar_read {
+ int rwsi_sz;
+ int rwgs_sz;
+};
+
+static const struct rtw_sar_read sar_read_hp = {
+ .rwsi_sz = sizeof(struct rtw_sar_rwsi_hp),
+ .rwgs_sz = sizeof(struct rtw_sar_rwgs_hp),
+};
+
+static const struct rtw_sar_read sar_read_rt = {
+ .rwsi_sz = sizeof(struct rtw_sar_rwsi_rt),
+ .rwgs_sz = sizeof(struct rtw_sar_rwgs_rt),
+};
+
+static u8 *rtw_sar_get_raw_package(struct rtw_dev *rtwdev,
+ union acpi_object *obj, int *len)
+{
+ u8 *raw;
+ u32 i;
+
+ if (obj->type != ACPI_TYPE_PACKAGE || obj->package.count <= 0) {
+ rtw_dbg(rtwdev, RTW_DBG_REGD,
+ "SAR: Unsupported obj to dump\n");
+ return NULL;
+ }
+
+ raw = kmalloc(obj->package.count, GFP_KERNEL);
+ if (!raw)
+ return NULL;
+
+ for (i = 0; i < obj->package.count; i++) {
+ union acpi_object *element;
+
+ element = &obj->package.elements[i];
+
+ if (element->type != ACPI_TYPE_INTEGER) {
+ rtw_dbg(rtwdev, RTW_DBG_REGD,
+ "SAR: Unexpected element type\n");
+ kfree(raw);
+ return NULL;
+ }
+
+ raw[i] = (u8)element->integer.value;
+ }
+
+ *len = obj->package.count;
+
+ return raw;
+}
+
+static void *rtw_sar_get_raw_table(struct rtw_dev *rtwdev, const char *method,
+ int *len)
+{
+ union acpi_object *obj;
+ u8 *raw;
+
+ obj = rtw_sar_get_acpiobj(rtwdev, method);
+ if (!obj)
+ return NULL;
+
+ raw = rtw_sar_get_raw_package(rtwdev, obj, len);
+ kfree(obj);
+
+ return raw;
+}
+
+static bool is_valid_rwrd(struct rtw_dev *rtwdev, const struct rtw_sar_rwrd *rwrd,
+ int len)
+{
+ if (len < sizeof(*rwrd)) {
+ rtw_dbg(rtwdev, RTW_DBG_REGD,
+ "SAR: RWRD: len %d is too short\n", len);
+ return false;
+ }
+
+ switch (rwrd->id) {
+ case RTW_SAR_RWRD_ID_HP:
+ rtwdev->sar.read = &sar_read_hp;
+ break;
+ case RTW_SAR_RWRD_ID_RT:
+ rtwdev->sar.read = &sar_read_rt;
+ break;
+ default:
+ rtw_dbg(rtwdev, RTW_DBG_REGD,
+ "SAR: RWRD: ID %04x isn't supported\n", rwrd->id);
+ return false;
+ }
+
+ if (sizeof(*rwrd) + rwrd->count * sizeof(rwrd->mode[0]) != len) {
+ rtw_dbg(rtwdev, RTW_DBG_REGD,
+ "SAR: RWRD: len(%d) doesn't match count(%d)\n",
+ len, rwrd->count);
+ return false;
+ }
+
+ return true;
+}
+
+static bool is_valid_rwsi_idx(struct rtw_dev *rtwdev, const struct rtw_sar_rwrd *rwrd,
+ const u8 index[], int len)
+{
+ /* index range is one based. i.e. 1 <= index[] <= rwrd->count */
+ int i;
+
+ for (i = 0; i < len; i++)
+ if (index[i] < 1 || index[i] > rwrd->count) {
+ rtw_dbg(rtwdev, RTW_DBG_REGD,
+ "SAR: RWSI: index is out of range\n");
+ return false;
+ }
+
+ return true;
+}
+
+static bool is_valid_rwsi(struct rtw_dev *rtwdev, const struct rtw_sar_rwrd *rwrd,
+ const union rtw_sar_rwsi *rwsi, int len)
+{
+ const struct rtw_sar_read *r = rtwdev->sar.read;
+
+ if (r->rwsi_sz != len)
+ goto err;
+
+ if (rwrd->id == RTW_SAR_RWRD_ID_HP &&
+ is_valid_rwsi_idx(rtwdev, rwrd, rwsi->hp.index, RTW_SAR_RWRD_CHAIN_NR))
+ return true;
+
+ if (rwrd->id == RTW_SAR_RWRD_ID_RT &&
+ is_valid_rwsi_idx(rtwdev, rwrd, &rwsi->rt.index, 1)) {
+ return true;
+ }
+
+err:
+ rtw_dbg(rtwdev, RTW_DBG_REGD,
+ "SAR: RWSI: len doesn't match struct size\n");
+
+ return false;
+}
+
+static bool is_valid_rwgs(struct rtw_dev *rtwdev, const struct rtw_sar_rwrd *rwrd,
+ const union rtw_sar_rwgs *rwgs, int len)
+{
+ const struct rtw_sar_read *r = rtwdev->sar.read;
+
+ if (r->rwgs_sz == len)
+ return true;
+
+ rtw_dbg(rtwdev, RTW_DBG_REGD,
+ "SAR: RWGS: len doesn't match struct size\n");
+
+ return false;
+}
+
+static int rtw_sar_load_dynamic_tables(struct rtw_dev *rtwdev)
+{
+ struct rtw_sar_rwrd *rwrd;
+ union rtw_sar_rwsi *rwsi;
+ union rtw_sar_rwgs *rwgs;
+ int len;
+ bool valid;
+
+ rwrd = rtw_sar_get_raw_table(rtwdev, ACPI_RWRD_METHOD, &len);
+ if (!rwrd)
+ goto out;
+ valid = is_valid_rwrd(rtwdev, rwrd, len);
+ if (!valid)
+ goto out_rwrd;
+ if (!rwrd->en) {
+ rtw_dbg(rtwdev, RTW_DBG_REGD, "SAR: RWRD isn't enabled\n");
+ goto out_rwrd;
+ }
+
+ rwsi = rtw_sar_get_raw_table(rtwdev, ACPI_RWSI_METHOD, &len);
+ if (!rwsi)
+ goto out_rwrd;
+ valid = is_valid_rwsi(rtwdev, rwrd, rwsi, len);
+ if (!valid)
+ goto out_rwsi;
+
+ rwgs = rtw_sar_get_raw_table(rtwdev, ACPI_RWGS_METHOD, &len);
+ if (!rwgs)
+ goto out_rwsi;
+ valid = is_valid_rwgs(rtwdev, rwrd, rwgs, len);
+ if (!valid)
+ goto out_rwgs;
+
+ rtwdev->sar.rwrd = rwrd;
+ rtwdev->sar.rwsi = rwsi;
+ rtwdev->sar.rwgs = rwgs;
+
+ rtw_dbg(rtwdev, RTW_DBG_REGD, "SAR: RWRD/RWSI/RWGS is adopted\n");
+
+ return 0;
+
+out_rwgs:
+ kfree(rwgs);
+out_rwsi:
+ kfree(rwsi);
+out_rwrd:
+ kfree(rwrd);
+out:
+ return -ENOENT;
+}
#else
static int rtw_sar_load_static_tables(struct rtw_dev *rtwdev)
{
return -ENOENT;
}
+
+static int rtw_sar_load_dynamic_tables(struct rtw_dev *rtwdev)
+{
+ return -ENOENT;
+}
#endif /* CONFIG_ACPI */
void rtw_sar_load_table(struct rtw_dev *rtwdev)
{
+ int ret;
+
+ ret = rtw_sar_load_dynamic_tables(rtwdev);
+ if (!ret)
+ return; /* if dynamic SAR table is loaded, ignore static SAR table */
+
rtw_sar_load_static_tables(rtwdev);
}
+
+void rtw_sar_release_table(struct rtw_dev *rtwdev)
+{
+ kfree(rtwdev->sar.rwrd);
+ kfree(rtwdev->sar.rwsi);
+ kfree(rtwdev->sar.rwgs);
+}
@@ -6,5 +6,6 @@
#define __RTW_SAR_H_
void rtw_sar_load_table(struct rtw_dev *rtwdev);
+void rtw_sar_release_table(struct rtw_dev *rtwdev);
#endif