diff --git a/sys/contrib/dev/mediatek/mt76/mt7915/coredump.c b/sys/contrib/dev/mediatek/mt76/mt7915/coredump.c index 5daf2258dfe6..8c9a69837c86 100644 --- a/sys/contrib/dev/mediatek/mt76/mt7915/coredump.c +++ b/sys/contrib/dev/mediatek/mt76/mt7915/coredump.c @@ -1,411 +1,415 @@ // SPDX-License-Identifier: ISC /* Copyright (C) 2022 MediaTek Inc. */ +#if defined(__FreeBSD__) +#define LINUXKPI_PARAM_PREFIX mt7915_ +#endif + #include #include #include #include #include "coredump.h" static bool coredump_memdump; module_param(coredump_memdump, bool, 0644); MODULE_PARM_DESC(coredump_memdump, "Optional ability to dump firmware memory"); static const struct mt7915_mem_region mt7915_mem_regions[] = { { .start = 0xe003b400, .len = 0x00003bff, .name = "CRAM", }, }; static const struct mt7915_mem_region mt7916_mem_regions[] = { { .start = 0x00800000, .len = 0x0005ffff, .name = "ROM", }, { .start = 0x00900000, .len = 0x00013fff, .name = "ULM1", }, { .start = 0x02200000, .len = 0x0004ffff, .name = "ULM2", }, { .start = 0x02300000, .len = 0x0004ffff, .name = "ULM3", }, { .start = 0x00400000, .len = 0x00027fff, .name = "SRAM", }, { .start = 0xe0000000, .len = 0x00157fff, .name = "CRAM", }, }; static const struct mt7915_mem_region mt798x_mem_regions[] = { { .start = 0x00800000, .len = 0x0005ffff, .name = "ROM", }, { .start = 0x00900000, .len = 0x0000ffff, .name = "ULM1", }, { .start = 0x02200000, .len = 0x0004ffff, .name = "ULM2", }, { .start = 0x02300000, .len = 0x0004ffff, .name = "ULM3", }, { .start = 0x00400000, .len = 0x00017fff, .name = "SRAM", }, { .start = 0xe0000000, .len = 0x00113fff, .name = "CRAM", }, }; const struct mt7915_mem_region* mt7915_coredump_get_mem_layout(struct mt7915_dev *dev, u32 *num) { switch (mt76_chip(&dev->mt76)) { case 0x7915: *num = ARRAY_SIZE(mt7915_mem_regions); return &mt7915_mem_regions[0]; case 0x7981: case 0x7986: *num = ARRAY_SIZE(mt798x_mem_regions); return &mt798x_mem_regions[0]; case 0x7916: *num = ARRAY_SIZE(mt7916_mem_regions); return &mt7916_mem_regions[0]; default: return NULL; } } static int mt7915_coredump_get_mem_size(struct mt7915_dev *dev) { const struct mt7915_mem_region *mem_region; size_t size = 0; u32 num; int i; mem_region = mt7915_coredump_get_mem_layout(dev, &num); if (!mem_region) return 0; for (i = 0; i < num; i++) { size += mem_region->len; mem_region++; } /* reserve space for the headers */ size += num * sizeof(struct mt7915_mem_hdr); /* make sure it is aligned 4 bytes for debug message print out */ size = ALIGN(size, 4); return size; } struct mt7915_crash_data *mt7915_coredump_new(struct mt7915_dev *dev) { struct mt7915_crash_data *crash_data = dev->coredump.crash_data; lockdep_assert_held(&dev->dump_mutex); guid_gen(&crash_data->guid); ktime_get_real_ts64(&crash_data->timestamp); return crash_data; } static void mt7915_coredump_fw_state(struct mt7915_dev *dev, struct mt7915_coredump *dump, bool *exception) { u32 state, count, type; type = (u32)mt76_get_field(dev, MT_FW_EXCEPT_TYPE, GENMASK(7, 0)); state = (u32)mt76_get_field(dev, MT_FW_ASSERT_STAT, GENMASK(7, 0)); count = is_mt7915(&dev->mt76) ? (u32)mt76_get_field(dev, MT_FW_EXCEPT_COUNT, GENMASK(15, 8)) : (u32)mt76_get_field(dev, MT_FW_EXCEPT_COUNT, GENMASK(7, 0)); /* normal mode: driver can manually trigger assertĀ for detail info */ if (!count) strscpy(dump->fw_state, "normal", sizeof(dump->fw_state)); else if (state > 1 && (count == 1) && type == 5) strscpy(dump->fw_state, "assert", sizeof(dump->fw_state)); else if ((state > 1 && count == 1) || count > 1) strscpy(dump->fw_state, "exception", sizeof(dump->fw_state)); *exception = !!count; } static void mt7915_coredump_fw_trace(struct mt7915_dev *dev, struct mt7915_coredump *dump, bool exception) { u32 n, irq, sch, base = MT_FW_EINT_INFO; /* trap or run? */ dump->last_msg_id = mt76_rr(dev, MT_FW_LAST_MSG_ID); n = is_mt7915(&dev->mt76) ? (u32)mt76_get_field(dev, base, GENMASK(7, 0)) : (u32)mt76_get_field(dev, base, GENMASK(15, 8)); dump->eint_info_idx = n; irq = mt76_rr(dev, base + 0x8); n = is_mt7915(&dev->mt76) ? FIELD_GET(GENMASK(7, 0), irq) : FIELD_GET(GENMASK(23, 16), irq); dump->irq_info_idx = n; sch = mt76_rr(dev, MT_FW_SCHED_INFO); n = is_mt7915(&dev->mt76) ? FIELD_GET(GENMASK(7, 0), sch) : FIELD_GET(GENMASK(15, 8), sch); dump->sched_info_idx = n; if (exception) { u32 i, y; /* sched trace */ n = is_mt7915(&dev->mt76) ? FIELD_GET(GENMASK(15, 8), sch) : FIELD_GET(GENMASK(7, 0), sch); n = n > 60 ? 60 : n; strscpy(dump->trace_sched, "(sched_info) id, time", sizeof(dump->trace_sched)); for (y = dump->sched_info_idx, i = 0; i < n; i++, y++) { mt7915_memcpy_fromio(dev, dump->sched, base + 0xc + y * 12, sizeof(dump->sched)); y = y >= n ? 0 : y; } /* irq trace */ n = is_mt7915(&dev->mt76) ? FIELD_GET(GENMASK(15, 8), irq) : FIELD_GET(GENMASK(7, 0), irq); n = n > 60 ? 60 : n; strscpy(dump->trace_irq, "(irq_info) id, time", sizeof(dump->trace_irq)); for (y = dump->irq_info_idx, i = 0; i < n; i++, y++) { mt7915_memcpy_fromio(dev, dump->irq, base + 0x4 + y * 16, sizeof(dump->irq)); y = y >= n ? 0 : y; } } } static void mt7915_coredump_fw_stack(struct mt7915_dev *dev, struct mt7915_coredump *dump, bool exception) { u32 oldest, i, idx; /* stop call stack record */ if (!exception) mt76_clear(dev, 0x89050200, BIT(0)); oldest = (u32)mt76_get_field(dev, 0x89050200, GENMASK(20, 16)) + 2; for (i = 0; i < 16; i++) { idx = ((oldest + 2 * i + 1) % 32); dump->call_stack[i] = mt76_rr(dev, 0x89050204 + idx * 4); } /* start call stack record */ if (!exception) mt76_set(dev, 0x89050200, BIT(0)); } static void mt7915_coredump_fw_task(struct mt7915_dev *dev, struct mt7915_coredump *dump) { u32 offs = is_mt7915(&dev->mt76) ? 0xe0 : 0x170; strscpy(dump->task_qid, "(task queue id) read, write", sizeof(dump->task_qid)); dump->taskq[0].read = mt76_rr(dev, MT_FW_TASK_QID1); dump->taskq[0].write = mt76_rr(dev, MT_FW_TASK_QID1 - 4); dump->taskq[1].read = mt76_rr(dev, MT_FW_TASK_QID2); dump->taskq[1].write = mt76_rr(dev, MT_FW_TASK_QID2 - 4); strscpy(dump->task_info, "(task stack) start, end, size", sizeof(dump->task_info)); dump->taski[0].start = mt76_rr(dev, MT_FW_TASK_START); dump->taski[0].end = mt76_rr(dev, MT_FW_TASK_END); dump->taski[0].size = mt76_rr(dev, MT_FW_TASK_SIZE); dump->taski[1].start = mt76_rr(dev, MT_FW_TASK_START + offs); dump->taski[1].end = mt76_rr(dev, MT_FW_TASK_END + offs); dump->taski[1].size = mt76_rr(dev, MT_FW_TASK_SIZE + offs); } static void mt7915_coredump_fw_context(struct mt7915_dev *dev, struct mt7915_coredump *dump) { u32 count, idx, id; count = mt76_rr(dev, MT_FW_CIRQ_COUNT); /* current context */ if (!count) { strscpy(dump->fw_context, "(context) interrupt", sizeof(dump->fw_context)); idx = is_mt7915(&dev->mt76) ? (u32)mt76_get_field(dev, MT_FW_CIRQ_IDX, GENMASK(31, 16)) : (u32)mt76_get_field(dev, MT_FW_CIRQ_IDX, GENMASK(15, 0)); dump->context.idx = idx; dump->context.handler = mt76_rr(dev, MT_FW_CIRQ_LISR); } else { idx = mt76_rr(dev, MT_FW_TASK_IDX); id = mt76_rr(dev, MT_FW_TASK_ID); if (!id && idx == 3) { strscpy(dump->fw_context, "(context) idle", sizeof(dump->fw_context)); } else if (id && idx != 3) { strscpy(dump->fw_context, "(context) task", sizeof(dump->fw_context)); dump->context.idx = idx; dump->context.handler = id; } } } static struct mt7915_coredump *mt7915_coredump_build(struct mt7915_dev *dev) { struct mt7915_crash_data *crash_data = dev->coredump.crash_data; struct mt7915_coredump *dump; struct mt7915_coredump_mem *dump_mem; size_t len, sofar = 0, hdr_len = sizeof(*dump); unsigned char *buf; bool exception; len = hdr_len; if (coredump_memdump && crash_data->memdump_buf_len) len += sizeof(*dump_mem) + crash_data->memdump_buf_len; sofar += hdr_len; /* this is going to get big when we start dumping memory and such, * so go ahead and use vmalloc. */ buf = vzalloc(len); if (!buf) return NULL; mutex_lock(&dev->dump_mutex); dump = (struct mt7915_coredump *)(buf); dump->len = len; /* plain text */ strscpy(dump->magic, "mt76-crash-dump", sizeof(dump->magic)); strscpy(dump->kernel, init_utsname()->release, sizeof(dump->kernel)); strscpy(dump->fw_ver, dev->mt76.hw->wiphy->fw_version, sizeof(dump->fw_ver)); guid_copy(&dump->guid, &crash_data->guid); dump->tv_sec = crash_data->timestamp.tv_sec; dump->tv_nsec = crash_data->timestamp.tv_nsec; dump->device_id = mt76_chip(&dev->mt76); mt7915_coredump_fw_state(dev, dump, &exception); mt7915_coredump_fw_trace(dev, dump, exception); mt7915_coredump_fw_task(dev, dump); mt7915_coredump_fw_context(dev, dump); mt7915_coredump_fw_stack(dev, dump, exception); /* gather memory content */ dump_mem = (struct mt7915_coredump_mem *)(buf + sofar); dump_mem->len = crash_data->memdump_buf_len; if (coredump_memdump && crash_data->memdump_buf_len) memcpy(dump_mem->data, crash_data->memdump_buf, crash_data->memdump_buf_len); mutex_unlock(&dev->dump_mutex); return dump; } int mt7915_coredump_submit(struct mt7915_dev *dev) { struct mt7915_coredump *dump; dump = mt7915_coredump_build(dev); if (!dump) { dev_warn(dev->mt76.dev, "no crash dump data found\n"); return -ENODATA; } dev_coredumpv(dev->mt76.dev, dump, dump->len, GFP_KERNEL); return 0; } int mt7915_coredump_register(struct mt7915_dev *dev) { struct mt7915_crash_data *crash_data; crash_data = vzalloc(sizeof(*dev->coredump.crash_data)); if (!crash_data) return -ENOMEM; dev->coredump.crash_data = crash_data; if (coredump_memdump) { crash_data->memdump_buf_len = mt7915_coredump_get_mem_size(dev); if (!crash_data->memdump_buf_len) /* no memory content */ return 0; crash_data->memdump_buf = vzalloc(crash_data->memdump_buf_len); if (!crash_data->memdump_buf) { vfree(crash_data); return -ENOMEM; } } return 0; } void mt7915_coredump_unregister(struct mt7915_dev *dev) { if (dev->coredump.crash_data->memdump_buf) { vfree(dev->coredump.crash_data->memdump_buf); dev->coredump.crash_data->memdump_buf = NULL; dev->coredump.crash_data->memdump_buf_len = 0; } vfree(dev->coredump.crash_data); dev->coredump.crash_data = NULL; } diff --git a/sys/contrib/dev/mediatek/mt76/mt7915/eeprom.c b/sys/contrib/dev/mediatek/mt76/mt7915/eeprom.c index 0df2bd93c82e..9214286ca000 100644 --- a/sys/contrib/dev/mediatek/mt76/mt7915/eeprom.c +++ b/sys/contrib/dev/mediatek/mt76/mt7915/eeprom.c @@ -1,415 +1,419 @@ // SPDX-License-Identifier: ISC /* Copyright (C) 2020 MediaTek Inc. */ +#if defined(__FreeBSD__) +#define LINUXKPI_PARAM_PREFIX mt7915_ +#endif + #include #include #include "mt7915.h" #include "eeprom.h" static bool enable_6ghz; module_param(enable_6ghz, bool, 0644); MODULE_PARM_DESC(enable_6ghz, "Enable 6 GHz instead of 5 GHz on hardware that supports both"); static int mt7915_eeprom_load_precal(struct mt7915_dev *dev) { struct mt76_dev *mdev = &dev->mt76; u8 *eeprom = mdev->eeprom.data; u32 offs = is_mt7915(&dev->mt76) ? MT_EE_DO_PRE_CAL : MT_EE_DO_PRE_CAL_V2; u32 size, val = eeprom[offs]; int ret; if (!dev->flash_mode || !val) return 0; size = mt7915_get_cal_group_size(dev) + mt7915_get_cal_dpd_size(dev); dev->cal = devm_kzalloc(mdev->dev, size, GFP_KERNEL); if (!dev->cal) return -ENOMEM; offs = is_mt7915(&dev->mt76) ? MT_EE_PRECAL : MT_EE_PRECAL_V2; ret = mt76_get_of_data_from_mtd(mdev, dev->cal, offs, size); if (!ret) return ret; ret = mt76_get_of_data_from_nvmem(mdev, dev->cal, "precal", size); if (!ret) return ret; dev_warn(mdev->dev, "missing precal data, size=%d\n", size); devm_kfree(mdev->dev, dev->cal); dev->cal = NULL; return ret; } static int mt7915_check_eeprom(struct mt7915_dev *dev) { u8 *eeprom = dev->mt76.eeprom.data; u16 val = get_unaligned_le16(eeprom); #define CHECK_EEPROM_ERR(match) (match ? 0 : -EINVAL) switch (val) { case 0x7915: return CHECK_EEPROM_ERR(is_mt7915(&dev->mt76)); case 0x7916: return CHECK_EEPROM_ERR(is_mt7916(&dev->mt76)); case 0x7981: return CHECK_EEPROM_ERR(is_mt7981(&dev->mt76)); case 0x7986: return CHECK_EEPROM_ERR(is_mt7986(&dev->mt76)); default: return -EINVAL; } } static char *mt7915_eeprom_name(struct mt7915_dev *dev) { switch (mt76_chip(&dev->mt76)) { case 0x7915: return dev->dbdc_support ? MT7915_EEPROM_DEFAULT_DBDC : MT7915_EEPROM_DEFAULT; case 0x7981: /* mt7981 only supports mt7976 and only in DBDC mode */ return MT7981_EEPROM_MT7976_DEFAULT_DBDC; case 0x7986: switch (mt7915_check_adie(dev, true)) { case MT7976_ONE_ADIE_DBDC: return MT7986_EEPROM_MT7976_DEFAULT_DBDC; case MT7975_ONE_ADIE: return MT7986_EEPROM_MT7975_DEFAULT; case MT7976_ONE_ADIE: return MT7986_EEPROM_MT7976_DEFAULT; case MT7975_DUAL_ADIE: return MT7986_EEPROM_MT7975_DUAL_DEFAULT; case MT7976_DUAL_ADIE: return MT7986_EEPROM_MT7976_DUAL_DEFAULT; default: break; } return NULL; default: return MT7916_EEPROM_DEFAULT; } } static int mt7915_eeprom_load_default(struct mt7915_dev *dev) { u8 *eeprom = dev->mt76.eeprom.data; const struct firmware *fw = NULL; int ret; ret = request_firmware(&fw, mt7915_eeprom_name(dev), dev->mt76.dev); if (ret) return ret; if (!fw || !fw->data) { dev_err(dev->mt76.dev, "Invalid default bin\n"); ret = -EINVAL; goto out; } memcpy(eeprom, fw->data, mt7915_eeprom_size(dev)); dev->flash_mode = true; out: release_firmware(fw); return ret; } static int mt7915_eeprom_load(struct mt7915_dev *dev) { int ret; u16 eeprom_size = mt7915_eeprom_size(dev); ret = mt76_eeprom_init(&dev->mt76, eeprom_size); if (ret < 0) return ret; if (ret) { dev->flash_mode = true; } else { u8 free_block_num; u32 block_num, i; u32 eeprom_blk_size = MT7915_EEPROM_BLOCK_SIZE; ret = mt7915_mcu_get_eeprom_free_block(dev, &free_block_num); if (ret < 0) return ret; /* efuse info isn't enough */ if (free_block_num >= 29) return -EINVAL; /* read eeprom data from efuse */ block_num = DIV_ROUND_UP(eeprom_size, eeprom_blk_size); for (i = 0; i < block_num; i++) { ret = mt7915_mcu_get_eeprom(dev, i * eeprom_blk_size, NULL); if (ret < 0) return ret; } } return mt7915_check_eeprom(dev); } static void mt7915_eeprom_parse_band_config(struct mt7915_phy *phy) { struct mt7915_dev *dev = phy->dev; u8 *eeprom = dev->mt76.eeprom.data; u8 band = phy->mt76->band_idx; u32 val; val = eeprom[MT_EE_WIFI_CONF + band]; val = FIELD_GET(MT_EE_WIFI_CONF0_BAND_SEL, val); if (!is_mt7915(&dev->mt76)) { switch (val) { case MT_EE_V2_BAND_SEL_5GHZ: phy->mt76->cap.has_5ghz = true; return; case MT_EE_V2_BAND_SEL_6GHZ: phy->mt76->cap.has_6ghz = true; return; case MT_EE_V2_BAND_SEL_5GHZ_6GHZ: if (enable_6ghz) { phy->mt76->cap.has_6ghz = true; u8p_replace_bits(&eeprom[MT_EE_WIFI_CONF + band], MT_EE_V2_BAND_SEL_6GHZ, MT_EE_WIFI_CONF0_BAND_SEL); } else { phy->mt76->cap.has_5ghz = true; u8p_replace_bits(&eeprom[MT_EE_WIFI_CONF + band], MT_EE_V2_BAND_SEL_5GHZ, MT_EE_WIFI_CONF0_BAND_SEL); } /* force to buffer mode */ dev->flash_mode = true; return; default: phy->mt76->cap.has_2ghz = true; return; } } else if (val == MT_EE_BAND_SEL_DEFAULT && dev->dbdc_support) { val = band ? MT_EE_BAND_SEL_5GHZ : MT_EE_BAND_SEL_2GHZ; } switch (val) { case MT_EE_BAND_SEL_5GHZ: phy->mt76->cap.has_5ghz = true; break; case MT_EE_BAND_SEL_2GHZ: phy->mt76->cap.has_2ghz = true; break; default: phy->mt76->cap.has_2ghz = true; phy->mt76->cap.has_5ghz = true; break; } } void mt7915_eeprom_parse_hw_cap(struct mt7915_dev *dev, struct mt7915_phy *phy) { u8 path, nss, nss_max = 4, *eeprom = dev->mt76.eeprom.data; struct mt76_phy *mphy = phy->mt76; u8 band = phy->mt76->band_idx; mt7915_eeprom_parse_band_config(phy); /* read tx/rx path from eeprom */ if (is_mt7915(&dev->mt76)) { path = FIELD_GET(MT_EE_WIFI_CONF0_TX_PATH, eeprom[MT_EE_WIFI_CONF]); } else { path = FIELD_GET(MT_EE_WIFI_CONF0_TX_PATH, eeprom[MT_EE_WIFI_CONF + band]); } if (!path || path > 4) path = 4; /* read tx/rx stream */ nss = path; if (dev->dbdc_support) { if (is_mt7915(&dev->mt76)) { path = min_t(u8, path, 2); nss = FIELD_GET(MT_EE_WIFI_CONF3_TX_PATH_B0, eeprom[MT_EE_WIFI_CONF + 3]); if (band) nss = FIELD_GET(MT_EE_WIFI_CONF3_TX_PATH_B1, eeprom[MT_EE_WIFI_CONF + 3]); } else { nss = FIELD_GET(MT_EE_WIFI_CONF_STREAM_NUM, eeprom[MT_EE_WIFI_CONF + 2 + band]); } if (!is_mt798x(&dev->mt76)) nss_max = 2; } if (!nss) nss = nss_max; nss = min_t(u8, min_t(u8, nss_max, nss), path); mphy->chainmask = BIT(path) - 1; if (band) mphy->chainmask <<= dev->chainshift; mphy->antenna_mask = BIT(nss) - 1; dev->chainmask |= mphy->chainmask; dev->chainshift = hweight8(dev->mphy.chainmask); } int mt7915_eeprom_init(struct mt7915_dev *dev) { int ret; ret = mt7915_eeprom_load(dev); if (ret < 0) { if (ret != -EINVAL) return ret; dev_warn(dev->mt76.dev, "eeprom load fail, use default bin\n"); ret = mt7915_eeprom_load_default(dev); if (ret) return ret; } mt7915_eeprom_load_precal(dev); mt7915_eeprom_parse_hw_cap(dev, &dev->phy); #if defined(__linux__) memcpy(dev->mphy.macaddr, dev->mt76.eeprom.data + MT_EE_MAC_ADDR, #elif defined(__FreeBSD__) memcpy(dev->mphy.macaddr, (u8 *)dev->mt76.eeprom.data + MT_EE_MAC_ADDR, #endif ETH_ALEN); mt76_eeprom_override(&dev->mphy); return 0; } int mt7915_eeprom_get_target_power(struct mt7915_dev *dev, struct ieee80211_channel *chan, u8 chain_idx) { u8 *eeprom = dev->mt76.eeprom.data; int index, target_power; bool tssi_on, is_7976; if (chain_idx > 3) return -EINVAL; tssi_on = mt7915_tssi_enabled(dev, chan->band); is_7976 = mt7915_check_adie(dev, false) || is_mt7916(&dev->mt76); if (chan->band == NL80211_BAND_2GHZ) { if (is_7976) { index = MT_EE_TX0_POWER_2G_V2 + chain_idx; target_power = eeprom[index]; } else { index = MT_EE_TX0_POWER_2G + chain_idx * 3; target_power = eeprom[index]; if (!tssi_on) target_power += eeprom[index + 1]; } } else if (chan->band == NL80211_BAND_5GHZ) { int group = mt7915_get_channel_group_5g(chan->hw_value, is_7976); if (is_7976) { index = MT_EE_TX0_POWER_5G_V2 + chain_idx * 5; target_power = eeprom[index + group]; } else { index = MT_EE_TX0_POWER_5G + chain_idx * 12; target_power = eeprom[index + group]; if (!tssi_on) target_power += eeprom[index + 8]; } } else { int group = mt7915_get_channel_group_6g(chan->hw_value); index = MT_EE_TX0_POWER_6G_V2 + chain_idx * 8; target_power = is_7976 ? eeprom[index + group] : 0; } return target_power; } s8 mt7915_eeprom_get_power_delta(struct mt7915_dev *dev, int band) { u8 *eeprom = dev->mt76.eeprom.data; u32 val, offs; s8 delta; bool is_7976 = mt7915_check_adie(dev, false) || is_mt7916(&dev->mt76); if (band == NL80211_BAND_2GHZ) offs = is_7976 ? MT_EE_RATE_DELTA_2G_V2 : MT_EE_RATE_DELTA_2G; else if (band == NL80211_BAND_5GHZ) offs = is_7976 ? MT_EE_RATE_DELTA_5G_V2 : MT_EE_RATE_DELTA_5G; else offs = is_7976 ? MT_EE_RATE_DELTA_6G_V2 : 0; val = eeprom[offs]; if (!offs || !(val & MT_EE_RATE_DELTA_EN)) return 0; delta = FIELD_GET(MT_EE_RATE_DELTA_MASK, val); return val & MT_EE_RATE_DELTA_SIGN ? delta : -delta; } bool mt7915_eeprom_has_background_radar(struct mt7915_dev *dev) { u8 val, buf[MT7915_EEPROM_BLOCK_SIZE]; u8 band_sel, tx_path, rx_path; int offs = MT_EE_WIFI_CONF + 1; switch (mt76_chip(&dev->mt76)) { case 0x7915: return true; case 0x7906: /* read efuse to check background radar capability */ if (mt7915_mcu_get_eeprom(dev, offs, buf)) break; val = buf[offs % MT7915_EEPROM_BLOCK_SIZE]; band_sel = u8_get_bits(val, MT_EE_WIFI_CONF0_BAND_SEL); tx_path = u8_get_bits(val, MT_EE_WIFI_CONF0_TX_PATH); rx_path = u8_get_bits(val, MT_EE_WIFI_CONF0_RX_PATH); return (band_sel == MT_EE_V2_BAND_SEL_5GHZ && tx_path == rx_path && rx_path == 2); case 0x7981: case 0x7986: default: break; } return false; } const u8 mt7915_sku_group_len[] = { [SKU_CCK] = 4, [SKU_OFDM] = 8, [SKU_HT_BW20] = 8, [SKU_HT_BW40] = 9, [SKU_VHT_BW20] = 12, [SKU_VHT_BW40] = 12, [SKU_VHT_BW80] = 12, [SKU_VHT_BW160] = 12, [SKU_HE_RU26] = 12, [SKU_HE_RU52] = 12, [SKU_HE_RU106] = 12, [SKU_HE_RU242] = 12, [SKU_HE_RU484] = 12, [SKU_HE_RU996] = 12, [SKU_HE_RU2x996] = 12 }; diff --git a/sys/contrib/dev/mediatek/mt76/mt7915/mcu.c b/sys/contrib/dev/mediatek/mt76/mt7915/mcu.c index f83f4eaa8a24..17c9c261960f 100644 --- a/sys/contrib/dev/mediatek/mt76/mt7915/mcu.c +++ b/sys/contrib/dev/mediatek/mt76/mt7915/mcu.c @@ -1,4049 +1,4053 @@ // SPDX-License-Identifier: ISC /* Copyright (C) 2020 MediaTek Inc. */ +#if defined(__FreeBSD__) +#define LINUXKPI_PARAM_PREFIX mt7915_ +#endif + #include #include "mt7915.h" #include "mcu.h" #include "mac.h" #include "eeprom.h" #define fw_name(_dev, name, ...) ({ \ char *_fw; \ switch (mt76_chip(&(_dev)->mt76)) { \ case 0x7915: \ _fw = MT7915_##name; \ break; \ case 0x7981: \ _fw = MT7981_##name; \ break; \ case 0x7986: \ _fw = MT7986_##name##__VA_ARGS__; \ break; \ default: \ _fw = MT7916_##name; \ break; \ } \ _fw; \ }) #define fw_name_var(_dev, name) (mt7915_check_adie(dev, false) ? \ fw_name(_dev, name) : \ fw_name(_dev, name, _MT7975)) #define MCU_PATCH_ADDRESS 0x200000 #define HE_PHY(p, c) u8_get_bits(c, IEEE80211_HE_PHY_##p) #define HE_MAC(m, c) u8_get_bits(c, IEEE80211_HE_MAC_##m) static bool sr_scene_detect = true; module_param(sr_scene_detect, bool, 0644); MODULE_PARM_DESC(sr_scene_detect, "Enable firmware scene detection algorithm"); static u8 mt7915_mcu_get_sta_nss(u16 mcs_map) { u8 nss; for (nss = 8; nss > 0; nss--) { u8 nss_mcs = (mcs_map >> (2 * (nss - 1))) & 3; if (nss_mcs != IEEE80211_VHT_MCS_NOT_SUPPORTED) break; } return nss - 1; } static void mt7915_mcu_set_sta_he_mcs(struct ieee80211_sta *sta, __le16 *he_mcs, u16 mcs_map) { struct mt7915_sta *msta = (struct mt7915_sta *)sta->drv_priv; struct mt7915_dev *dev = msta->vif->phy->dev; enum nl80211_band band = msta->vif->phy->mt76->chandef.chan->band; const u16 *mask = msta->vif->bitrate_mask.control[band].he_mcs; int nss, max_nss = sta->deflink.rx_nss > 3 ? 4 : sta->deflink.rx_nss; for (nss = 0; nss < max_nss; nss++) { int mcs; switch ((mcs_map >> (2 * nss)) & 0x3) { case IEEE80211_HE_MCS_SUPPORT_0_11: mcs = GENMASK(11, 0); break; case IEEE80211_HE_MCS_SUPPORT_0_9: mcs = GENMASK(9, 0); break; case IEEE80211_HE_MCS_SUPPORT_0_7: mcs = GENMASK(7, 0); break; default: mcs = 0; } mcs = mcs ? fls(mcs & mask[nss]) - 1 : -1; switch (mcs) { case 0 ... 7: mcs = IEEE80211_HE_MCS_SUPPORT_0_7; break; case 8 ... 9: mcs = IEEE80211_HE_MCS_SUPPORT_0_9; break; case 10 ... 11: mcs = IEEE80211_HE_MCS_SUPPORT_0_11; break; default: mcs = IEEE80211_HE_MCS_NOT_SUPPORTED; break; } mcs_map &= ~(0x3 << (nss * 2)); mcs_map |= mcs << (nss * 2); /* only support 2ss on 160MHz for mt7915 */ if (is_mt7915(&dev->mt76) && nss > 1 && sta->deflink.bandwidth == IEEE80211_STA_RX_BW_160) break; } *he_mcs = cpu_to_le16(mcs_map); } static void mt7915_mcu_set_sta_vht_mcs(struct ieee80211_sta *sta, __le16 *vht_mcs, const u16 *mask) { struct mt7915_sta *msta = (struct mt7915_sta *)sta->drv_priv; struct mt7915_dev *dev = msta->vif->phy->dev; u16 mcs_map = le16_to_cpu(sta->deflink.vht_cap.vht_mcs.rx_mcs_map); int nss, max_nss = sta->deflink.rx_nss > 3 ? 4 : sta->deflink.rx_nss; u16 mcs; for (nss = 0; nss < max_nss; nss++, mcs_map >>= 2) { switch (mcs_map & 0x3) { case IEEE80211_VHT_MCS_SUPPORT_0_9: mcs = GENMASK(9, 0); break; case IEEE80211_VHT_MCS_SUPPORT_0_8: mcs = GENMASK(8, 0); break; case IEEE80211_VHT_MCS_SUPPORT_0_7: mcs = GENMASK(7, 0); break; default: mcs = 0; } vht_mcs[nss] = cpu_to_le16(mcs & mask[nss]); /* only support 2ss on 160MHz for mt7915 */ if (is_mt7915(&dev->mt76) && nss > 1 && sta->deflink.bandwidth == IEEE80211_STA_RX_BW_160) break; } } static void mt7915_mcu_set_sta_ht_mcs(struct ieee80211_sta *sta, u8 *ht_mcs, const u8 *mask) { int nss, max_nss = sta->deflink.rx_nss > 3 ? 4 : sta->deflink.rx_nss; for (nss = 0; nss < max_nss; nss++) ht_mcs[nss] = sta->deflink.ht_cap.mcs.rx_mask[nss] & mask[nss]; } static int mt7915_mcu_parse_response(struct mt76_dev *mdev, int cmd, struct sk_buff *skb, int seq) { struct mt7915_dev *dev = container_of(mdev, struct mt7915_dev, mt76); struct mt76_connac2_mcu_rxd *rxd; int ret = 0; if (!skb) { dev_err(mdev->dev, "Message %08x (seq %d) timeout\n", cmd, seq); if (!test_and_set_bit(MT76_MCU_RESET, &dev->mphy.state)) { dev->recovery.restart = true; wake_up(&dev->mt76.mcu.wait); queue_work(dev->mt76.wq, &dev->reset_work); wake_up(&dev->reset_wait); } return -ETIMEDOUT; } rxd = (struct mt76_connac2_mcu_rxd *)skb->data; if (seq != rxd->seq && !(rxd->eid == MCU_CMD_EXT_CID && rxd->ext_eid == MCU_EXT_EVENT_WA_TX_STAT)) return -EAGAIN; if (cmd == MCU_CMD(PATCH_SEM_CONTROL)) { skb_pull(skb, sizeof(*rxd) - 4); ret = *skb->data; } else if (cmd == MCU_EXT_CMD(THERMAL_CTRL)) { skb_pull(skb, sizeof(*rxd) + 4); ret = le32_to_cpu(*(__le32 *)skb->data); } else { skb_pull(skb, sizeof(struct mt76_connac2_mcu_rxd)); } return ret; } static void mt7915_mcu_set_timeout(struct mt76_dev *mdev, int cmd) { mdev->mcu.timeout = 5 * HZ; if ((cmd & __MCU_CMD_FIELD_ID) != MCU_CMD_EXT_CID) return; switch (FIELD_GET(__MCU_CMD_FIELD_EXT_ID, cmd)) { case MCU_EXT_CMD_THERMAL_CTRL: case MCU_EXT_CMD_GET_MIB_INFO: case MCU_EXT_CMD_PHY_STAT_INFO: case MCU_EXT_CMD_STA_REC_UPDATE: case MCU_EXT_CMD_BSS_INFO_UPDATE: mdev->mcu.timeout = 2 * HZ; return; case MCU_EXT_CMD_EFUSE_BUFFER_MODE: mdev->mcu.timeout = 10 * HZ; return; default: break; } } static int mt7915_mcu_send_message(struct mt76_dev *mdev, struct sk_buff *skb, int cmd, int *wait_seq) { struct mt7915_dev *dev = container_of(mdev, struct mt7915_dev, mt76); enum mt76_mcuq_id qid; if (cmd == MCU_CMD(FW_SCATTER)) qid = MT_MCUQ_FWDL; else if (test_bit(MT76_STATE_MCU_RUNNING, &dev->mphy.state)) qid = MT_MCUQ_WA; else qid = MT_MCUQ_WM; mt7915_mcu_set_timeout(mdev, cmd); return mt76_tx_queue_skb_raw(dev, mdev->q_mcu[qid], skb, 0); } int mt7915_mcu_wa_cmd(struct mt7915_dev *dev, int cmd, u32 a1, u32 a2, u32 a3) { struct { __le32 args[3]; } req = { .args = { cpu_to_le32(a1), cpu_to_le32(a2), cpu_to_le32(a3), }, }; return mt76_mcu_send_msg(&dev->mt76, cmd, &req, sizeof(req), false); } static void mt7915_mcu_csa_finish(void *priv, u8 *mac, struct ieee80211_vif *vif) { if (!vif->bss_conf.csa_active || vif->type == NL80211_IFTYPE_STATION) return; ieee80211_csa_finish(vif, 0); } static void mt7915_mcu_rx_csa_notify(struct mt7915_dev *dev, struct sk_buff *skb) { struct mt76_phy *mphy = &dev->mt76.phy; struct mt7915_mcu_csa_notify *c; c = (struct mt7915_mcu_csa_notify *)skb->data; if (c->band_idx > MT_BAND1) return; if ((c->band_idx && !dev->phy.mt76->band_idx) && dev->mt76.phys[MT_BAND1]) mphy = dev->mt76.phys[MT_BAND1]; ieee80211_iterate_active_interfaces_atomic(mphy->hw, IEEE80211_IFACE_ITER_RESUME_ALL, mt7915_mcu_csa_finish, mphy->hw); } static void mt7915_mcu_rx_thermal_notify(struct mt7915_dev *dev, struct sk_buff *skb) { struct mt76_phy *mphy = &dev->mt76.phy; struct mt7915_mcu_thermal_notify *t; struct mt7915_phy *phy; t = (struct mt7915_mcu_thermal_notify *)skb->data; if (t->ctrl.ctrl_id != THERMAL_PROTECT_ENABLE) return; if (t->ctrl.band_idx > MT_BAND1) return; if ((t->ctrl.band_idx && !dev->phy.mt76->band_idx) && dev->mt76.phys[MT_BAND1]) mphy = dev->mt76.phys[MT_BAND1]; phy = mphy->priv; phy->throttle_state = t->ctrl.duty.duty_cycle; } static void mt7915_mcu_rx_radar_detected(struct mt7915_dev *dev, struct sk_buff *skb) { struct mt76_phy *mphy = &dev->mt76.phy; struct mt7915_mcu_rdd_report *r; u32 sku; r = (struct mt7915_mcu_rdd_report *)skb->data; switch (r->rdd_idx) { case MT_RDD_IDX_BAND0: break; case MT_RDD_IDX_BAND1: sku = mt7915_check_adie(dev, true); /* the main phy is bound to band 1 for this sku */ if (is_mt7986(&dev->mt76) && (sku == MT7975_ONE_ADIE || sku == MT7976_ONE_ADIE)) break; mphy = dev->mt76.phys[MT_BAND1]; break; case MT_RDD_IDX_BACKGROUND: if (!dev->rdd2_phy) return; mphy = dev->rdd2_phy->mt76; break; default: dev_err(dev->mt76.dev, "Unknown RDD idx %d\n", r->rdd_idx); return; } if (!mphy) return; if (r->rdd_idx == MT_RDD_IDX_BACKGROUND) cfg80211_background_radar_event(mphy->hw->wiphy, &dev->rdd2_chandef, GFP_ATOMIC); else ieee80211_radar_detected(mphy->hw, NULL); dev->hw_pattern++; } static void mt7915_mcu_rx_log_message(struct mt7915_dev *dev, struct sk_buff *skb) { struct mt76_connac2_mcu_rxd *rxd; int len = skb->len - sizeof(*rxd); const char *data, *type; rxd = (struct mt76_connac2_mcu_rxd *)skb->data; data = (char *)&rxd[1]; switch (rxd->s2d_index) { case 0: #if !defined(__FreeBSD__) || defined(CONFIG_MT7915_DEBUGFS) if (mt7915_debugfs_rx_log(dev, data, len)) return; #endif type = "WM"; break; case 2: type = "WA"; break; default: type = "unknown"; break; } wiphy_info(mt76_hw(dev)->wiphy, "%s: %.*s", type, len, data); } static void mt7915_mcu_cca_finish(void *priv, u8 *mac, struct ieee80211_vif *vif) { if (!vif->bss_conf.color_change_active || vif->type == NL80211_IFTYPE_STATION) return; ieee80211_color_change_finish(vif, 0); } static void mt7915_mcu_rx_bcc_notify(struct mt7915_dev *dev, struct sk_buff *skb) { struct mt76_phy *mphy = &dev->mt76.phy; struct mt7915_mcu_bcc_notify *b; b = (struct mt7915_mcu_bcc_notify *)skb->data; if (b->band_idx > MT_BAND1) return; if ((b->band_idx && !dev->phy.mt76->band_idx) && dev->mt76.phys[MT_BAND1]) mphy = dev->mt76.phys[MT_BAND1]; ieee80211_iterate_active_interfaces_atomic(mphy->hw, IEEE80211_IFACE_ITER_RESUME_ALL, mt7915_mcu_cca_finish, mphy->hw); } static void mt7915_mcu_rx_ext_event(struct mt7915_dev *dev, struct sk_buff *skb) { struct mt76_connac2_mcu_rxd *rxd; rxd = (struct mt76_connac2_mcu_rxd *)skb->data; switch (rxd->ext_eid) { case MCU_EXT_EVENT_THERMAL_PROTECT: mt7915_mcu_rx_thermal_notify(dev, skb); break; case MCU_EXT_EVENT_RDD_REPORT: mt7915_mcu_rx_radar_detected(dev, skb); break; case MCU_EXT_EVENT_CSA_NOTIFY: mt7915_mcu_rx_csa_notify(dev, skb); break; case MCU_EXT_EVENT_FW_LOG_2_HOST: mt7915_mcu_rx_log_message(dev, skb); break; case MCU_EXT_EVENT_BCC_NOTIFY: mt7915_mcu_rx_bcc_notify(dev, skb); break; default: break; } } static void mt7915_mcu_rx_unsolicited_event(struct mt7915_dev *dev, struct sk_buff *skb) { struct mt76_connac2_mcu_rxd *rxd; rxd = (struct mt76_connac2_mcu_rxd *)skb->data; switch (rxd->eid) { case MCU_EVENT_EXT: mt7915_mcu_rx_ext_event(dev, skb); break; default: break; } dev_kfree_skb(skb); } void mt7915_mcu_rx_event(struct mt7915_dev *dev, struct sk_buff *skb) { struct mt76_connac2_mcu_rxd *rxd; rxd = (struct mt76_connac2_mcu_rxd *)skb->data; if ((rxd->ext_eid == MCU_EXT_EVENT_THERMAL_PROTECT || rxd->ext_eid == MCU_EXT_EVENT_FW_LOG_2_HOST || rxd->ext_eid == MCU_EXT_EVENT_ASSERT_DUMP || rxd->ext_eid == MCU_EXT_EVENT_PS_SYNC || rxd->ext_eid == MCU_EXT_EVENT_BCC_NOTIFY || !rxd->seq) && !(rxd->eid == MCU_CMD_EXT_CID && rxd->ext_eid == MCU_EXT_EVENT_WA_TX_STAT)) mt7915_mcu_rx_unsolicited_event(dev, skb); else mt76_mcu_rx_event(&dev->mt76, skb); } static struct tlv * mt7915_mcu_add_nested_subtlv(struct sk_buff *skb, int sub_tag, int sub_len, __le16 *sub_ntlv, __le16 *len) { struct tlv *ptlv, tlv = { .tag = cpu_to_le16(sub_tag), .len = cpu_to_le16(sub_len), }; ptlv = skb_put_zero(skb, sub_len); memcpy(ptlv, &tlv, sizeof(tlv)); le16_add_cpu(sub_ntlv, 1); le16_add_cpu(len, sub_len); return ptlv; } /** bss info **/ struct mt7915_he_obss_narrow_bw_ru_data { bool tolerated; }; static void mt7915_check_he_obss_narrow_bw_ru_iter(struct wiphy *wiphy, struct cfg80211_bss *bss, void *_data) { struct mt7915_he_obss_narrow_bw_ru_data *data = _data; const struct element *elem; rcu_read_lock(); elem = ieee80211_bss_get_elem(bss, WLAN_EID_EXT_CAPABILITY); if (!elem || elem->datalen <= 10 || !(elem->data[10] & WLAN_EXT_CAPA10_OBSS_NARROW_BW_RU_TOLERANCE_SUPPORT)) data->tolerated = false; rcu_read_unlock(); } static bool mt7915_check_he_obss_narrow_bw_ru(struct ieee80211_hw *hw, struct ieee80211_vif *vif) { struct mt7915_he_obss_narrow_bw_ru_data iter_data = { .tolerated = true, }; if (!(vif->bss_conf.chanreq.oper.chan->flags & IEEE80211_CHAN_RADAR)) return false; cfg80211_bss_iter(hw->wiphy, &vif->bss_conf.chanreq.oper, mt7915_check_he_obss_narrow_bw_ru_iter, &iter_data); /* * If there is at least one AP on radar channel that cannot * tolerate 26-tone RU UL OFDMA transmissions using HE TB PPDU. */ return !iter_data.tolerated; } static void mt7915_mcu_bss_rfch_tlv(struct sk_buff *skb, struct ieee80211_vif *vif, struct mt7915_phy *phy) { struct cfg80211_chan_def *chandef = &phy->mt76->chandef; struct bss_info_rf_ch *ch; struct tlv *tlv; int freq1 = chandef->center_freq1; tlv = mt76_connac_mcu_add_tlv(skb, BSS_INFO_RF_CH, sizeof(*ch)); ch = (struct bss_info_rf_ch *)tlv; ch->pri_ch = chandef->chan->hw_value; ch->center_ch0 = ieee80211_frequency_to_channel(freq1); ch->bw = mt76_connac_chan_bw(chandef); if (chandef->width == NL80211_CHAN_WIDTH_80P80) { int freq2 = chandef->center_freq2; ch->center_ch1 = ieee80211_frequency_to_channel(freq2); } if (vif->bss_conf.he_support && vif->type == NL80211_IFTYPE_STATION) { struct mt76_phy *mphy = phy->mt76; ch->he_ru26_block = mt7915_check_he_obss_narrow_bw_ru(mphy->hw, vif); ch->he_all_disable = false; } else { ch->he_all_disable = true; } } static void mt7915_mcu_bss_ra_tlv(struct sk_buff *skb, struct ieee80211_vif *vif, struct mt7915_phy *phy) { int max_nss = hweight8(phy->mt76->antenna_mask); struct bss_info_ra *ra; struct tlv *tlv; tlv = mt76_connac_mcu_add_tlv(skb, BSS_INFO_RA, sizeof(*ra)); ra = (struct bss_info_ra *)tlv; ra->op_mode = vif->type == NL80211_IFTYPE_AP; ra->adhoc_en = vif->type == NL80211_IFTYPE_ADHOC; ra->short_preamble = true; ra->tx_streams = max_nss; ra->rx_streams = max_nss; ra->algo = 4; ra->train_up_rule = 2; ra->train_up_high_thres = 110; ra->train_up_rule_rssi = -70; ra->low_traffic_thres = 2; ra->phy_cap = cpu_to_le32(0xfdf); ra->interval = cpu_to_le32(500); ra->fast_interval = cpu_to_le32(100); } static void mt7915_mcu_bss_he_tlv(struct sk_buff *skb, struct ieee80211_vif *vif, struct mt7915_phy *phy) { #define DEFAULT_HE_PE_DURATION 4 #define DEFAULT_HE_DURATION_RTS_THRES 1023 const struct ieee80211_sta_he_cap *cap; struct bss_info_he *he; struct tlv *tlv; cap = mt76_connac_get_he_phy_cap(phy->mt76, vif); tlv = mt76_connac_mcu_add_tlv(skb, BSS_INFO_HE_BASIC, sizeof(*he)); he = (struct bss_info_he *)tlv; he->he_pe_duration = vif->bss_conf.htc_trig_based_pkt_ext; if (!he->he_pe_duration) he->he_pe_duration = DEFAULT_HE_PE_DURATION; he->he_rts_thres = cpu_to_le16(vif->bss_conf.frame_time_rts_th); if (!he->he_rts_thres) he->he_rts_thres = cpu_to_le16(DEFAULT_HE_DURATION_RTS_THRES); he->max_nss_mcs[CMD_HE_MCS_BW80] = cap->he_mcs_nss_supp.tx_mcs_80; he->max_nss_mcs[CMD_HE_MCS_BW160] = cap->he_mcs_nss_supp.tx_mcs_160; he->max_nss_mcs[CMD_HE_MCS_BW8080] = cap->he_mcs_nss_supp.tx_mcs_80p80; } static void mt7915_mcu_bss_hw_amsdu_tlv(struct sk_buff *skb) { #define TXD_CMP_MAP1 GENMASK(15, 0) #define TXD_CMP_MAP2 (GENMASK(31, 0) & ~BIT(23)) struct bss_info_hw_amsdu *amsdu; struct tlv *tlv; tlv = mt76_connac_mcu_add_tlv(skb, BSS_INFO_HW_AMSDU, sizeof(*amsdu)); amsdu = (struct bss_info_hw_amsdu *)tlv; amsdu->cmp_bitmap_0 = cpu_to_le32(TXD_CMP_MAP1); amsdu->cmp_bitmap_1 = cpu_to_le32(TXD_CMP_MAP2); amsdu->trig_thres = cpu_to_le16(2); amsdu->enable = true; } static void mt7915_mcu_bss_bmc_tlv(struct sk_buff *skb, struct mt7915_phy *phy) { struct bss_info_bmc_rate *bmc; struct cfg80211_chan_def *chandef = &phy->mt76->chandef; enum nl80211_band band = chandef->chan->band; struct tlv *tlv; tlv = mt76_connac_mcu_add_tlv(skb, BSS_INFO_BMC_RATE, sizeof(*bmc)); bmc = (struct bss_info_bmc_rate *)tlv; if (band == NL80211_BAND_2GHZ) { bmc->short_preamble = true; } else { bmc->bc_trans = cpu_to_le16(0x2000); bmc->mc_trans = cpu_to_le16(0x2080); } } static int mt7915_mcu_muar_config(struct mt7915_phy *phy, struct ieee80211_vif *vif, bool bssid, bool enable) { struct mt7915_dev *dev = phy->dev; struct mt7915_vif *mvif = (struct mt7915_vif *)vif->drv_priv; u32 idx = mvif->mt76.omac_idx - REPEATER_BSSID_START; u32 mask = phy->omac_mask >> 32 & ~BIT(idx); const u8 *addr = vif->addr; struct { u8 mode; u8 force_clear; u8 clear_bitmap[8]; u8 entry_count; u8 write; u8 band; u8 index; u8 bssid; u8 addr[ETH_ALEN]; } __packed req = { .mode = !!mask || enable, .entry_count = 1, .write = 1, .band = phy->mt76->band_idx, .index = idx * 2 + bssid, }; if (bssid) addr = vif->bss_conf.bssid; if (enable) ether_addr_copy(req.addr, addr); return mt76_mcu_send_msg(&dev->mt76, MCU_EXT_CMD(MUAR_UPDATE), &req, sizeof(req), true); } int mt7915_mcu_add_bss_info(struct mt7915_phy *phy, struct ieee80211_vif *vif, int enable) { struct mt7915_vif *mvif = (struct mt7915_vif *)vif->drv_priv; struct mt7915_dev *dev = phy->dev; struct sk_buff *skb; if (mvif->mt76.omac_idx >= REPEATER_BSSID_START) { mt7915_mcu_muar_config(phy, vif, false, enable); mt7915_mcu_muar_config(phy, vif, true, enable); } skb = __mt76_connac_mcu_alloc_sta_req(&dev->mt76, &mvif->mt76, NULL, MT7915_BSS_UPDATE_MAX_SIZE); if (IS_ERR(skb)) return PTR_ERR(skb); /* bss_omac must be first */ if (enable) mt76_connac_mcu_bss_omac_tlv(skb, vif); mt76_connac_mcu_bss_basic_tlv(skb, vif, NULL, phy->mt76, mvif->sta.wcid.idx, enable); if (vif->type == NL80211_IFTYPE_MONITOR) goto out; if (enable) { mt7915_mcu_bss_rfch_tlv(skb, vif, phy); mt7915_mcu_bss_bmc_tlv(skb, phy); mt7915_mcu_bss_ra_tlv(skb, vif, phy); mt7915_mcu_bss_hw_amsdu_tlv(skb); if (vif->bss_conf.he_support) mt7915_mcu_bss_he_tlv(skb, vif, phy); if (mvif->mt76.omac_idx >= EXT_BSSID_START && mvif->mt76.omac_idx < REPEATER_BSSID_START) mt76_connac_mcu_bss_ext_tlv(skb, &mvif->mt76); } out: return mt76_mcu_skb_send_msg(&dev->mt76, skb, MCU_EXT_CMD(BSS_INFO_UPDATE), true); } /** starec & wtbl **/ int mt7915_mcu_add_tx_ba(struct mt7915_dev *dev, struct ieee80211_ampdu_params *params, bool enable) { struct mt7915_sta *msta = (struct mt7915_sta *)params->sta->drv_priv; struct mt7915_vif *mvif = msta->vif; int ret; mt76_worker_disable(&dev->mt76.tx_worker); if (enable && !params->amsdu) msta->wcid.amsdu = false; ret = mt76_connac_mcu_sta_ba(&dev->mt76, &mvif->mt76, params, MCU_EXT_CMD(STA_REC_UPDATE), enable, true); mt76_worker_enable(&dev->mt76.tx_worker); return ret; } int mt7915_mcu_add_rx_ba(struct mt7915_dev *dev, struct ieee80211_ampdu_params *params, bool enable) { struct mt7915_sta *msta = (struct mt7915_sta *)params->sta->drv_priv; struct mt7915_vif *mvif = msta->vif; return mt76_connac_mcu_sta_ba(&dev->mt76, &mvif->mt76, params, MCU_EXT_CMD(STA_REC_UPDATE), enable, false); } static void mt7915_mcu_sta_he_tlv(struct sk_buff *skb, struct ieee80211_sta *sta, struct ieee80211_vif *vif) { struct mt7915_vif *mvif = (struct mt7915_vif *)vif->drv_priv; struct ieee80211_he_cap_elem *elem = &sta->deflink.he_cap.he_cap_elem; struct ieee80211_he_mcs_nss_supp mcs_map; struct sta_rec_he *he; struct tlv *tlv; u32 cap = 0; if (!sta->deflink.he_cap.has_he) return; tlv = mt76_connac_mcu_add_tlv(skb, STA_REC_HE, sizeof(*he)); he = (struct sta_rec_he *)tlv; if (elem->mac_cap_info[0] & IEEE80211_HE_MAC_CAP0_HTC_HE) cap |= STA_REC_HE_CAP_HTC; if (elem->mac_cap_info[2] & IEEE80211_HE_MAC_CAP2_BSR) cap |= STA_REC_HE_CAP_BSR; if (elem->mac_cap_info[3] & IEEE80211_HE_MAC_CAP3_OMI_CONTROL) cap |= STA_REC_HE_CAP_OM; if (elem->mac_cap_info[4] & IEEE80211_HE_MAC_CAP4_AMSDU_IN_AMPDU) cap |= STA_REC_HE_CAP_AMSDU_IN_AMPDU; if (elem->mac_cap_info[4] & IEEE80211_HE_MAC_CAP4_BQR) cap |= STA_REC_HE_CAP_BQR; if (elem->phy_cap_info[0] & (IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_RU_MAPPING_IN_2G | IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_RU_MAPPING_IN_5G)) cap |= STA_REC_HE_CAP_BW20_RU242_SUPPORT; if (mvif->cap.he_ldpc && (elem->phy_cap_info[1] & IEEE80211_HE_PHY_CAP1_LDPC_CODING_IN_PAYLOAD)) cap |= STA_REC_HE_CAP_LDPC; if (elem->phy_cap_info[1] & IEEE80211_HE_PHY_CAP1_HE_LTF_AND_GI_FOR_HE_PPDUS_0_8US) cap |= STA_REC_HE_CAP_SU_PPDU_1LTF_8US_GI; if (elem->phy_cap_info[2] & IEEE80211_HE_PHY_CAP2_NDP_4x_LTF_AND_3_2US) cap |= STA_REC_HE_CAP_NDP_4LTF_3DOT2MS_GI; if (elem->phy_cap_info[2] & IEEE80211_HE_PHY_CAP2_STBC_TX_UNDER_80MHZ) cap |= STA_REC_HE_CAP_LE_EQ_80M_TX_STBC; if (elem->phy_cap_info[2] & IEEE80211_HE_PHY_CAP2_STBC_RX_UNDER_80MHZ) cap |= STA_REC_HE_CAP_LE_EQ_80M_RX_STBC; if (elem->phy_cap_info[6] & IEEE80211_HE_PHY_CAP6_TRIG_CQI_FB) cap |= STA_REC_HE_CAP_TRIG_CQI_FK; if (elem->phy_cap_info[6] & IEEE80211_HE_PHY_CAP6_PARTIAL_BW_EXT_RANGE) cap |= STA_REC_HE_CAP_PARTIAL_BW_EXT_RANGE; if (elem->phy_cap_info[7] & IEEE80211_HE_PHY_CAP7_HE_SU_MU_PPDU_4XLTF_AND_08_US_GI) cap |= STA_REC_HE_CAP_SU_MU_PPDU_4LTF_8US_GI; if (elem->phy_cap_info[7] & IEEE80211_HE_PHY_CAP7_STBC_TX_ABOVE_80MHZ) cap |= STA_REC_HE_CAP_GT_80M_TX_STBC; if (elem->phy_cap_info[7] & IEEE80211_HE_PHY_CAP7_STBC_RX_ABOVE_80MHZ) cap |= STA_REC_HE_CAP_GT_80M_RX_STBC; if (elem->phy_cap_info[8] & IEEE80211_HE_PHY_CAP8_HE_ER_SU_PPDU_4XLTF_AND_08_US_GI) cap |= STA_REC_HE_CAP_ER_SU_PPDU_4LTF_8US_GI; if (elem->phy_cap_info[8] & IEEE80211_HE_PHY_CAP8_HE_ER_SU_1XLTF_AND_08_US_GI) cap |= STA_REC_HE_CAP_ER_SU_PPDU_1LTF_8US_GI; if (elem->phy_cap_info[9] & IEEE80211_HE_PHY_CAP9_TX_1024_QAM_LESS_THAN_242_TONE_RU) cap |= STA_REC_HE_CAP_TX_1024QAM_UNDER_RU242; if (elem->phy_cap_info[9] & IEEE80211_HE_PHY_CAP9_RX_1024_QAM_LESS_THAN_242_TONE_RU) cap |= STA_REC_HE_CAP_RX_1024QAM_UNDER_RU242; he->he_cap = cpu_to_le32(cap); mcs_map = sta->deflink.he_cap.he_mcs_nss_supp; switch (sta->deflink.bandwidth) { case IEEE80211_STA_RX_BW_160: if (elem->phy_cap_info[0] & IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_80PLUS80_MHZ_IN_5G) mt7915_mcu_set_sta_he_mcs(sta, &he->max_nss_mcs[CMD_HE_MCS_BW8080], le16_to_cpu(mcs_map.rx_mcs_80p80)); mt7915_mcu_set_sta_he_mcs(sta, &he->max_nss_mcs[CMD_HE_MCS_BW160], le16_to_cpu(mcs_map.rx_mcs_160)); fallthrough; default: mt7915_mcu_set_sta_he_mcs(sta, &he->max_nss_mcs[CMD_HE_MCS_BW80], le16_to_cpu(mcs_map.rx_mcs_80)); break; } he->t_frame_dur = HE_MAC(CAP1_TF_MAC_PAD_DUR_MASK, elem->mac_cap_info[1]); he->max_ampdu_exp = HE_MAC(CAP3_MAX_AMPDU_LEN_EXP_MASK, elem->mac_cap_info[3]); he->bw_set = HE_PHY(CAP0_CHANNEL_WIDTH_SET_MASK, elem->phy_cap_info[0]); he->device_class = HE_PHY(CAP1_DEVICE_CLASS_A, elem->phy_cap_info[1]); he->punc_pream_rx = HE_PHY(CAP1_PREAMBLE_PUNC_RX_MASK, elem->phy_cap_info[1]); he->dcm_tx_mode = HE_PHY(CAP3_DCM_MAX_CONST_TX_MASK, elem->phy_cap_info[3]); he->dcm_tx_max_nss = HE_PHY(CAP3_DCM_MAX_TX_NSS_2, elem->phy_cap_info[3]); he->dcm_rx_mode = HE_PHY(CAP3_DCM_MAX_CONST_RX_MASK, elem->phy_cap_info[3]); he->dcm_rx_max_nss = HE_PHY(CAP3_DCM_MAX_RX_NSS_2, elem->phy_cap_info[3]); he->dcm_rx_max_nss = HE_PHY(CAP8_DCM_MAX_RU_MASK, elem->phy_cap_info[8]); he->pkt_ext = 2; } static void mt7915_mcu_sta_muru_tlv(struct mt7915_dev *dev, struct sk_buff *skb, struct ieee80211_sta *sta, struct ieee80211_vif *vif) { struct mt7915_vif *mvif = (struct mt7915_vif *)vif->drv_priv; struct ieee80211_he_cap_elem *elem = &sta->deflink.he_cap.he_cap_elem; struct sta_rec_muru *muru; struct tlv *tlv; if (vif->type != NL80211_IFTYPE_STATION && vif->type != NL80211_IFTYPE_AP) return; tlv = mt76_connac_mcu_add_tlv(skb, STA_REC_MURU, sizeof(*muru)); muru = (struct sta_rec_muru *)tlv; muru->cfg.mimo_dl_en = mvif->cap.he_mu_ebfer || mvif->cap.vht_mu_ebfer || mvif->cap.vht_mu_ebfee; if (!is_mt7915(&dev->mt76)) muru->cfg.mimo_ul_en = true; muru->cfg.ofdma_dl_en = true; if (sta->deflink.vht_cap.vht_supported) muru->mimo_dl.vht_mu_bfee = !!(sta->deflink.vht_cap.cap & IEEE80211_VHT_CAP_MU_BEAMFORMEE_CAPABLE); if (!sta->deflink.he_cap.has_he) return; muru->mimo_dl.partial_bw_dl_mimo = HE_PHY(CAP6_PARTIAL_BANDWIDTH_DL_MUMIMO, elem->phy_cap_info[6]); muru->mimo_ul.full_ul_mimo = HE_PHY(CAP2_UL_MU_FULL_MU_MIMO, elem->phy_cap_info[2]); muru->mimo_ul.partial_ul_mimo = HE_PHY(CAP2_UL_MU_PARTIAL_MU_MIMO, elem->phy_cap_info[2]); muru->ofdma_dl.punc_pream_rx = HE_PHY(CAP1_PREAMBLE_PUNC_RX_MASK, elem->phy_cap_info[1]); muru->ofdma_dl.he_20m_in_40m_2g = HE_PHY(CAP8_20MHZ_IN_40MHZ_HE_PPDU_IN_2G, elem->phy_cap_info[8]); muru->ofdma_dl.he_20m_in_160m = HE_PHY(CAP8_20MHZ_IN_160MHZ_HE_PPDU, elem->phy_cap_info[8]); muru->ofdma_dl.he_80m_in_160m = HE_PHY(CAP8_80MHZ_IN_160MHZ_HE_PPDU, elem->phy_cap_info[8]); muru->ofdma_ul.t_frame_dur = HE_MAC(CAP1_TF_MAC_PAD_DUR_MASK, elem->mac_cap_info[1]); muru->ofdma_ul.mu_cascading = HE_MAC(CAP2_MU_CASCADING, elem->mac_cap_info[2]); muru->ofdma_ul.uo_ra = HE_MAC(CAP3_OFDMA_RA, elem->mac_cap_info[3]); muru->ofdma_ul.rx_ctrl_frame_to_mbss = HE_MAC(CAP3_RX_CTRL_FRAME_TO_MULTIBSS, elem->mac_cap_info[3]); } static void mt7915_mcu_sta_ht_tlv(struct sk_buff *skb, struct ieee80211_sta *sta) { struct sta_rec_ht *ht; struct tlv *tlv; if (!sta->deflink.ht_cap.ht_supported) return; tlv = mt76_connac_mcu_add_tlv(skb, STA_REC_HT, sizeof(*ht)); ht = (struct sta_rec_ht *)tlv; ht->ht_cap = cpu_to_le16(sta->deflink.ht_cap.cap); } static void mt7915_mcu_sta_vht_tlv(struct sk_buff *skb, struct ieee80211_sta *sta) { struct sta_rec_vht *vht; struct tlv *tlv; if (!sta->deflink.vht_cap.vht_supported) return; tlv = mt76_connac_mcu_add_tlv(skb, STA_REC_VHT, sizeof(*vht)); vht = (struct sta_rec_vht *)tlv; vht->vht_cap = cpu_to_le32(sta->deflink.vht_cap.cap); vht->vht_rx_mcs_map = sta->deflink.vht_cap.vht_mcs.rx_mcs_map; vht->vht_tx_mcs_map = sta->deflink.vht_cap.vht_mcs.tx_mcs_map; } static void mt7915_mcu_sta_amsdu_tlv(struct mt7915_dev *dev, struct sk_buff *skb, struct ieee80211_vif *vif, struct ieee80211_sta *sta) { struct mt7915_sta *msta = (struct mt7915_sta *)sta->drv_priv; struct sta_rec_amsdu *amsdu; struct tlv *tlv; if (vif->type != NL80211_IFTYPE_STATION && vif->type != NL80211_IFTYPE_AP) return; if (!sta->deflink.agg.max_amsdu_len) return; tlv = mt76_connac_mcu_add_tlv(skb, STA_REC_HW_AMSDU, sizeof(*amsdu)); amsdu = (struct sta_rec_amsdu *)tlv; amsdu->max_amsdu_num = 8; amsdu->amsdu_en = true; msta->wcid.amsdu = true; switch (sta->deflink.agg.max_amsdu_len) { case IEEE80211_MAX_MPDU_LEN_VHT_11454: if (!is_mt7915(&dev->mt76)) { amsdu->max_mpdu_size = IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_11454; return; } fallthrough; case IEEE80211_MAX_MPDU_LEN_HT_7935: case IEEE80211_MAX_MPDU_LEN_VHT_7991: amsdu->max_mpdu_size = IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_7991; return; default: amsdu->max_mpdu_size = IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_3895; return; } } static int mt7915_mcu_sta_wtbl_tlv(struct mt7915_dev *dev, struct sk_buff *skb, struct ieee80211_vif *vif, struct ieee80211_sta *sta) { struct mt7915_vif *mvif = (struct mt7915_vif *)vif->drv_priv; struct mt7915_sta *msta; struct wtbl_req_hdr *wtbl_hdr; struct mt76_wcid *wcid; struct tlv *tlv; msta = sta ? (struct mt7915_sta *)sta->drv_priv : &mvif->sta; wcid = sta ? &msta->wcid : NULL; tlv = mt76_connac_mcu_add_tlv(skb, STA_REC_WTBL, sizeof(struct tlv)); wtbl_hdr = mt76_connac_mcu_alloc_wtbl_req(&dev->mt76, &msta->wcid, WTBL_RESET_AND_SET, tlv, &skb); if (IS_ERR(wtbl_hdr)) return PTR_ERR(wtbl_hdr); mt76_connac_mcu_wtbl_generic_tlv(&dev->mt76, skb, vif, sta, tlv, wtbl_hdr); mt76_connac_mcu_wtbl_hdr_trans_tlv(skb, vif, wcid, tlv, wtbl_hdr); if (sta) mt76_connac_mcu_wtbl_ht_tlv(&dev->mt76, skb, sta, tlv, wtbl_hdr, mvif->cap.ht_ldpc, mvif->cap.vht_ldpc); return 0; } static inline bool mt7915_is_ebf_supported(struct mt7915_phy *phy, struct ieee80211_vif *vif, struct ieee80211_sta *sta, bool bfee) { struct mt7915_vif *mvif = (struct mt7915_vif *)vif->drv_priv; int sts = hweight16(phy->mt76->chainmask); if (vif->type != NL80211_IFTYPE_STATION && vif->type != NL80211_IFTYPE_AP) return false; if (!bfee && sts < 2) return false; if (sta->deflink.he_cap.has_he) { struct ieee80211_he_cap_elem *pe = &sta->deflink.he_cap.he_cap_elem; if (bfee) return mvif->cap.he_su_ebfee && HE_PHY(CAP3_SU_BEAMFORMER, pe->phy_cap_info[3]); else return mvif->cap.he_su_ebfer && HE_PHY(CAP4_SU_BEAMFORMEE, pe->phy_cap_info[4]); } if (sta->deflink.vht_cap.vht_supported) { u32 cap = sta->deflink.vht_cap.cap; if (bfee) return mvif->cap.vht_su_ebfee && (cap & IEEE80211_VHT_CAP_SU_BEAMFORMER_CAPABLE); else return mvif->cap.vht_su_ebfer && (cap & IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE); } return false; } static void mt7915_mcu_sta_sounding_rate(struct sta_rec_bf *bf) { bf->sounding_phy = MT_PHY_TYPE_OFDM; bf->ndp_rate = 0; /* mcs0 */ bf->ndpa_rate = MT7915_CFEND_RATE_DEFAULT; /* ofdm 24m */ bf->rept_poll_rate = MT7915_CFEND_RATE_DEFAULT; /* ofdm 24m */ } static void mt7915_mcu_sta_bfer_ht(struct ieee80211_sta *sta, struct mt7915_phy *phy, struct sta_rec_bf *bf) { struct ieee80211_mcs_info *mcs = &sta->deflink.ht_cap.mcs; u8 n = 0; bf->tx_mode = MT_PHY_TYPE_HT; if ((mcs->tx_params & IEEE80211_HT_MCS_TX_RX_DIFF) && (mcs->tx_params & IEEE80211_HT_MCS_TX_DEFINED)) n = FIELD_GET(IEEE80211_HT_MCS_TX_MAX_STREAMS_MASK, mcs->tx_params); else if (mcs->rx_mask[3]) n = 3; else if (mcs->rx_mask[2]) n = 2; else if (mcs->rx_mask[1]) n = 1; bf->nrow = hweight8(phy->mt76->chainmask) - 1; bf->ncol = min_t(u8, bf->nrow, n); bf->ibf_ncol = n; } static void mt7915_mcu_sta_bfer_vht(struct ieee80211_sta *sta, struct mt7915_phy *phy, struct sta_rec_bf *bf, bool explicit) { struct ieee80211_sta_vht_cap *pc = &sta->deflink.vht_cap; struct ieee80211_sta_vht_cap *vc = &phy->mt76->sband_5g.sband.vht_cap; u16 mcs_map = le16_to_cpu(pc->vht_mcs.rx_mcs_map); u8 nss_mcs = mt7915_mcu_get_sta_nss(mcs_map); u8 tx_ant = hweight8(phy->mt76->chainmask) - 1; bf->tx_mode = MT_PHY_TYPE_VHT; if (explicit) { u8 sts, snd_dim; mt7915_mcu_sta_sounding_rate(bf); sts = FIELD_GET(IEEE80211_VHT_CAP_BEAMFORMEE_STS_MASK, pc->cap); snd_dim = FIELD_GET(IEEE80211_VHT_CAP_SOUNDING_DIMENSIONS_MASK, vc->cap); bf->nrow = min_t(u8, min_t(u8, snd_dim, sts), tx_ant); bf->ncol = min_t(u8, nss_mcs, bf->nrow); bf->ibf_ncol = bf->ncol; if (sta->deflink.bandwidth == IEEE80211_STA_RX_BW_160) bf->nrow = 1; } else { bf->nrow = tx_ant; bf->ncol = min_t(u8, nss_mcs, bf->nrow); bf->ibf_ncol = nss_mcs; if (sta->deflink.bandwidth == IEEE80211_STA_RX_BW_160) bf->ibf_nrow = 1; } } static void mt7915_mcu_sta_bfer_he(struct ieee80211_sta *sta, struct ieee80211_vif *vif, struct mt7915_phy *phy, struct sta_rec_bf *bf) { struct ieee80211_sta_he_cap *pc = &sta->deflink.he_cap; struct ieee80211_he_cap_elem *pe = &pc->he_cap_elem; const struct ieee80211_sta_he_cap *vc = mt76_connac_get_he_phy_cap(phy->mt76, vif); const struct ieee80211_he_cap_elem *ve = &vc->he_cap_elem; u16 mcs_map = le16_to_cpu(pc->he_mcs_nss_supp.rx_mcs_80); u8 nss_mcs = mt7915_mcu_get_sta_nss(mcs_map); u8 snd_dim, sts; bf->tx_mode = MT_PHY_TYPE_HE_SU; mt7915_mcu_sta_sounding_rate(bf); bf->trigger_su = HE_PHY(CAP6_TRIG_SU_BEAMFORMING_FB, pe->phy_cap_info[6]); bf->trigger_mu = HE_PHY(CAP6_TRIG_MU_BEAMFORMING_PARTIAL_BW_FB, pe->phy_cap_info[6]); snd_dim = HE_PHY(CAP5_BEAMFORMEE_NUM_SND_DIM_UNDER_80MHZ_MASK, ve->phy_cap_info[5]); sts = HE_PHY(CAP4_BEAMFORMEE_MAX_STS_UNDER_80MHZ_MASK, pe->phy_cap_info[4]); bf->nrow = min_t(u8, snd_dim, sts); bf->ncol = min_t(u8, nss_mcs, bf->nrow); bf->ibf_ncol = bf->ncol; if (sta->deflink.bandwidth != IEEE80211_STA_RX_BW_160) return; /* go over for 160MHz and 80p80 */ if (pe->phy_cap_info[0] & IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_160MHZ_IN_5G) { mcs_map = le16_to_cpu(pc->he_mcs_nss_supp.rx_mcs_160); nss_mcs = mt7915_mcu_get_sta_nss(mcs_map); bf->ncol_gt_bw80 = nss_mcs; } if (pe->phy_cap_info[0] & IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_80PLUS80_MHZ_IN_5G) { mcs_map = le16_to_cpu(pc->he_mcs_nss_supp.rx_mcs_80p80); nss_mcs = mt7915_mcu_get_sta_nss(mcs_map); if (bf->ncol_gt_bw80) bf->ncol_gt_bw80 = min_t(u8, bf->ncol_gt_bw80, nss_mcs); else bf->ncol_gt_bw80 = nss_mcs; } snd_dim = HE_PHY(CAP5_BEAMFORMEE_NUM_SND_DIM_ABOVE_80MHZ_MASK, ve->phy_cap_info[5]); sts = HE_PHY(CAP4_BEAMFORMEE_MAX_STS_ABOVE_80MHZ_MASK, pe->phy_cap_info[4]); bf->nrow_gt_bw80 = min_t(int, snd_dim, sts); } static void mt7915_mcu_sta_bfer_tlv(struct mt7915_dev *dev, struct sk_buff *skb, struct ieee80211_vif *vif, struct ieee80211_sta *sta) { struct mt7915_vif *mvif = (struct mt7915_vif *)vif->drv_priv; struct mt7915_phy *phy = mvif->phy; int tx_ant = hweight8(phy->mt76->chainmask) - 1; struct sta_rec_bf *bf; struct tlv *tlv; static const u8 matrix[4][4] = { {0, 0, 0, 0}, {1, 1, 0, 0}, /* 2x1, 2x2, 2x3, 2x4 */ {2, 4, 4, 0}, /* 3x1, 3x2, 3x3, 3x4 */ {3, 5, 6, 0} /* 4x1, 4x2, 4x3, 4x4 */ }; bool ebf; if (!(sta->deflink.ht_cap.ht_supported || sta->deflink.he_cap.has_he)) return; ebf = mt7915_is_ebf_supported(phy, vif, sta, false); if (!ebf && !dev->ibf) return; tlv = mt76_connac_mcu_add_tlv(skb, STA_REC_BF, sizeof(*bf)); bf = (struct sta_rec_bf *)tlv; /* he: eBF only, in accordance with spec * vht: support eBF and iBF * ht: iBF only, since mac80211 lacks of eBF support */ if (sta->deflink.he_cap.has_he && ebf) mt7915_mcu_sta_bfer_he(sta, vif, phy, bf); else if (sta->deflink.vht_cap.vht_supported) mt7915_mcu_sta_bfer_vht(sta, phy, bf, ebf); else if (sta->deflink.ht_cap.ht_supported) mt7915_mcu_sta_bfer_ht(sta, phy, bf); else return; bf->bf_cap = ebf ? ebf : dev->ibf << 1; bf->bw = sta->deflink.bandwidth; bf->ibf_dbw = sta->deflink.bandwidth; bf->ibf_nrow = tx_ant; if (!ebf && sta->deflink.bandwidth <= IEEE80211_STA_RX_BW_40 && !bf->ncol) bf->ibf_timeout = 0x48; else bf->ibf_timeout = 0x18; if (ebf && bf->nrow != tx_ant) bf->mem_20m = matrix[tx_ant][bf->ncol]; else bf->mem_20m = matrix[bf->nrow][bf->ncol]; switch (sta->deflink.bandwidth) { case IEEE80211_STA_RX_BW_160: case IEEE80211_STA_RX_BW_80: bf->mem_total = bf->mem_20m * 2; break; case IEEE80211_STA_RX_BW_40: bf->mem_total = bf->mem_20m; break; case IEEE80211_STA_RX_BW_20: default: break; } } static void mt7915_mcu_sta_bfee_tlv(struct mt7915_dev *dev, struct sk_buff *skb, struct ieee80211_vif *vif, struct ieee80211_sta *sta) { struct mt7915_vif *mvif = (struct mt7915_vif *)vif->drv_priv; struct mt7915_phy *phy = mvif->phy; int tx_ant = hweight8(phy->mt76->chainmask) - 1; struct sta_rec_bfee *bfee; struct tlv *tlv; u8 nrow = 0; if (!(sta->deflink.vht_cap.vht_supported || sta->deflink.he_cap.has_he)) return; if (!mt7915_is_ebf_supported(phy, vif, sta, true)) return; tlv = mt76_connac_mcu_add_tlv(skb, STA_REC_BFEE, sizeof(*bfee)); bfee = (struct sta_rec_bfee *)tlv; if (sta->deflink.he_cap.has_he) { struct ieee80211_he_cap_elem *pe = &sta->deflink.he_cap.he_cap_elem; nrow = HE_PHY(CAP5_BEAMFORMEE_NUM_SND_DIM_UNDER_80MHZ_MASK, pe->phy_cap_info[5]); } else if (sta->deflink.vht_cap.vht_supported) { struct ieee80211_sta_vht_cap *pc = &sta->deflink.vht_cap; nrow = FIELD_GET(IEEE80211_VHT_CAP_SOUNDING_DIMENSIONS_MASK, pc->cap); } /* reply with identity matrix to avoid 2x2 BF negative gain */ bfee->fb_identity_matrix = (nrow == 1 && tx_ant == 2); } static enum mcu_mmps_mode mt7915_mcu_get_mmps_mode(enum ieee80211_smps_mode smps) { switch (smps) { case IEEE80211_SMPS_OFF: return MCU_MMPS_DISABLE; case IEEE80211_SMPS_STATIC: return MCU_MMPS_STATIC; case IEEE80211_SMPS_DYNAMIC: return MCU_MMPS_DYNAMIC; default: return MCU_MMPS_DISABLE; } } int mt7915_mcu_set_fixed_rate_ctrl(struct mt7915_dev *dev, struct ieee80211_vif *vif, struct ieee80211_sta *sta, void *data, u32 field) { struct mt7915_vif *mvif = (struct mt7915_vif *)vif->drv_priv; struct mt7915_sta *msta = (struct mt7915_sta *)sta->drv_priv; struct sta_phy *phy = data; struct sta_rec_ra_fixed *ra; struct sk_buff *skb; struct tlv *tlv; skb = mt76_connac_mcu_alloc_sta_req(&dev->mt76, &mvif->mt76, &msta->wcid); if (IS_ERR(skb)) return PTR_ERR(skb); tlv = mt76_connac_mcu_add_tlv(skb, STA_REC_RA_UPDATE, sizeof(*ra)); ra = (struct sta_rec_ra_fixed *)tlv; switch (field) { case RATE_PARAM_AUTO: break; case RATE_PARAM_FIXED: case RATE_PARAM_FIXED_MCS: case RATE_PARAM_FIXED_GI: case RATE_PARAM_FIXED_HE_LTF: if (phy) ra->phy = *phy; break; case RATE_PARAM_MMPS_UPDATE: ra->mmps_mode = mt7915_mcu_get_mmps_mode(sta->deflink.smps_mode); break; case RATE_PARAM_SPE_UPDATE: ra->spe_idx = *(u8 *)data; break; default: break; } ra->field = cpu_to_le32(field); return mt76_mcu_skb_send_msg(&dev->mt76, skb, MCU_EXT_CMD(STA_REC_UPDATE), true); } int mt7915_mcu_add_smps(struct mt7915_dev *dev, struct ieee80211_vif *vif, struct ieee80211_sta *sta) { struct mt7915_vif *mvif = (struct mt7915_vif *)vif->drv_priv; struct mt7915_sta *msta = (struct mt7915_sta *)sta->drv_priv; struct wtbl_req_hdr *wtbl_hdr; struct tlv *sta_wtbl; struct sk_buff *skb; int ret; skb = mt76_connac_mcu_alloc_sta_req(&dev->mt76, &mvif->mt76, &msta->wcid); if (IS_ERR(skb)) return PTR_ERR(skb); sta_wtbl = mt76_connac_mcu_add_tlv(skb, STA_REC_WTBL, sizeof(struct tlv)); wtbl_hdr = mt76_connac_mcu_alloc_wtbl_req(&dev->mt76, &msta->wcid, WTBL_SET, sta_wtbl, &skb); if (IS_ERR(wtbl_hdr)) return PTR_ERR(wtbl_hdr); mt76_connac_mcu_wtbl_smps_tlv(skb, sta, sta_wtbl, wtbl_hdr); ret = mt76_mcu_skb_send_msg(&dev->mt76, skb, MCU_EXT_CMD(STA_REC_UPDATE), true); if (ret) return ret; return mt7915_mcu_set_fixed_rate_ctrl(dev, vif, sta, NULL, RATE_PARAM_MMPS_UPDATE); } static int mt7915_mcu_set_spe_idx(struct mt7915_dev *dev, struct ieee80211_vif *vif, struct ieee80211_sta *sta) { struct mt7915_vif *mvif = (struct mt7915_vif *)vif->drv_priv; struct mt76_phy *mphy = mvif->phy->mt76; u8 spe_idx = mt76_connac_spe_idx(mphy->antenna_mask); return mt7915_mcu_set_fixed_rate_ctrl(dev, vif, sta, &spe_idx, RATE_PARAM_SPE_UPDATE); } static int mt7915_mcu_add_rate_ctrl_fixed(struct mt7915_dev *dev, struct ieee80211_vif *vif, struct ieee80211_sta *sta) { struct mt7915_vif *mvif = (struct mt7915_vif *)vif->drv_priv; struct cfg80211_chan_def *chandef = &mvif->phy->mt76->chandef; struct cfg80211_bitrate_mask *mask = &mvif->bitrate_mask; enum nl80211_band band = chandef->chan->band; struct sta_phy phy = {}; int ret, nrates = 0; #define __sta_phy_bitrate_mask_check(_mcs, _gi, _ht, _he) \ do { \ u8 i, gi = mask->control[band]._gi; \ gi = (_he) ? gi : gi == NL80211_TXRATE_FORCE_SGI; \ for (i = 0; i <= sta->deflink.bandwidth; i++) { \ phy.sgi |= gi << (i << (_he)); \ phy.he_ltf |= mask->control[band].he_ltf << (i << (_he));\ } \ for (i = 0; i < ARRAY_SIZE(mask->control[band]._mcs); i++) { \ if (!mask->control[band]._mcs[i]) \ continue; \ nrates += hweight16(mask->control[band]._mcs[i]); \ phy.mcs = ffs(mask->control[band]._mcs[i]) - 1; \ if (_ht) \ phy.mcs += 8 * i; \ } \ } while (0) if (sta->deflink.he_cap.has_he) { __sta_phy_bitrate_mask_check(he_mcs, he_gi, 0, 1); } else if (sta->deflink.vht_cap.vht_supported) { __sta_phy_bitrate_mask_check(vht_mcs, gi, 0, 0); } else if (sta->deflink.ht_cap.ht_supported) { __sta_phy_bitrate_mask_check(ht_mcs, gi, 1, 0); } else { nrates = hweight32(mask->control[band].legacy); phy.mcs = ffs(mask->control[band].legacy) - 1; } #undef __sta_phy_bitrate_mask_check /* fall back to auto rate control */ if (mask->control[band].gi == NL80211_TXRATE_DEFAULT_GI && mask->control[band].he_gi == GENMASK(7, 0) && mask->control[band].he_ltf == GENMASK(7, 0) && nrates != 1) return 0; /* fixed single rate */ if (nrates == 1) { ret = mt7915_mcu_set_fixed_rate_ctrl(dev, vif, sta, &phy, RATE_PARAM_FIXED_MCS); if (ret) return ret; } /* fixed GI */ if (mask->control[band].gi != NL80211_TXRATE_DEFAULT_GI || mask->control[band].he_gi != GENMASK(7, 0)) { struct mt7915_sta *msta = (struct mt7915_sta *)sta->drv_priv; u32 addr; /* firmware updates only TXCMD but doesn't take WTBL into * account, so driver should update here to reflect the * actual txrate hardware sends out. */ addr = mt7915_mac_wtbl_lmac_addr(dev, msta->wcid.idx, 7); if (sta->deflink.he_cap.has_he) mt76_rmw_field(dev, addr, GENMASK(31, 24), phy.sgi); else mt76_rmw_field(dev, addr, GENMASK(15, 12), phy.sgi); ret = mt7915_mcu_set_fixed_rate_ctrl(dev, vif, sta, &phy, RATE_PARAM_FIXED_GI); if (ret) return ret; } /* fixed HE_LTF */ if (mask->control[band].he_ltf != GENMASK(7, 0)) { ret = mt7915_mcu_set_fixed_rate_ctrl(dev, vif, sta, &phy, RATE_PARAM_FIXED_HE_LTF); if (ret) return ret; } return mt7915_mcu_set_spe_idx(dev, vif, sta); } static void mt7915_mcu_sta_rate_ctrl_tlv(struct sk_buff *skb, struct mt7915_dev *dev, struct ieee80211_vif *vif, struct ieee80211_sta *sta) { struct mt7915_vif *mvif = (struct mt7915_vif *)vif->drv_priv; struct mt76_phy *mphy = mvif->phy->mt76; struct cfg80211_chan_def *chandef = &mphy->chandef; struct cfg80211_bitrate_mask *mask = &mvif->bitrate_mask; enum nl80211_band band = chandef->chan->band; struct sta_rec_ra *ra; struct tlv *tlv; u32 supp_rate = sta->deflink.supp_rates[band]; u32 cap = sta->wme ? STA_CAP_WMM : 0; tlv = mt76_connac_mcu_add_tlv(skb, STA_REC_RA, sizeof(*ra)); ra = (struct sta_rec_ra *)tlv; ra->valid = true; ra->auto_rate = true; ra->phy_mode = mt76_connac_get_phy_mode(mphy, vif, band, &sta->deflink); ra->channel = chandef->chan->hw_value; ra->bw = sta->deflink.bandwidth; ra->phy.bw = sta->deflink.bandwidth; ra->mmps_mode = mt7915_mcu_get_mmps_mode(sta->deflink.smps_mode); if (supp_rate) { supp_rate &= mask->control[band].legacy; ra->rate_len = hweight32(supp_rate); if (band == NL80211_BAND_2GHZ) { ra->supp_mode = MODE_CCK; ra->supp_cck_rate = supp_rate & GENMASK(3, 0); if (ra->rate_len > 4) { ra->supp_mode |= MODE_OFDM; ra->supp_ofdm_rate = supp_rate >> 4; } } else { ra->supp_mode = MODE_OFDM; ra->supp_ofdm_rate = supp_rate; } } if (sta->deflink.ht_cap.ht_supported) { ra->supp_mode |= MODE_HT; ra->af = sta->deflink.ht_cap.ampdu_factor; ra->ht_gf = !!(sta->deflink.ht_cap.cap & IEEE80211_HT_CAP_GRN_FLD); cap |= STA_CAP_HT; if (sta->deflink.ht_cap.cap & IEEE80211_HT_CAP_SGI_20) cap |= STA_CAP_SGI_20; if (sta->deflink.ht_cap.cap & IEEE80211_HT_CAP_SGI_40) cap |= STA_CAP_SGI_40; if (sta->deflink.ht_cap.cap & IEEE80211_HT_CAP_TX_STBC) cap |= STA_CAP_TX_STBC; if (sta->deflink.ht_cap.cap & IEEE80211_HT_CAP_RX_STBC) cap |= STA_CAP_RX_STBC; if (mvif->cap.ht_ldpc && (sta->deflink.ht_cap.cap & IEEE80211_HT_CAP_LDPC_CODING)) cap |= STA_CAP_LDPC; mt7915_mcu_set_sta_ht_mcs(sta, ra->ht_mcs, mask->control[band].ht_mcs); ra->supp_ht_mcs = *(__le32 *)ra->ht_mcs; } if (sta->deflink.vht_cap.vht_supported) { u8 af; ra->supp_mode |= MODE_VHT; af = FIELD_GET(IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_MASK, sta->deflink.vht_cap.cap); ra->af = max_t(u8, ra->af, af); cap |= STA_CAP_VHT; if (sta->deflink.vht_cap.cap & IEEE80211_VHT_CAP_SHORT_GI_80) cap |= STA_CAP_VHT_SGI_80; if (sta->deflink.vht_cap.cap & IEEE80211_VHT_CAP_SHORT_GI_160) cap |= STA_CAP_VHT_SGI_160; if (sta->deflink.vht_cap.cap & IEEE80211_VHT_CAP_TXSTBC) cap |= STA_CAP_VHT_TX_STBC; if (sta->deflink.vht_cap.cap & IEEE80211_VHT_CAP_RXSTBC_1) cap |= STA_CAP_VHT_RX_STBC; if (mvif->cap.vht_ldpc && (sta->deflink.vht_cap.cap & IEEE80211_VHT_CAP_RXLDPC)) cap |= STA_CAP_VHT_LDPC; mt7915_mcu_set_sta_vht_mcs(sta, ra->supp_vht_mcs, mask->control[band].vht_mcs); } if (sta->deflink.he_cap.has_he) { ra->supp_mode |= MODE_HE; cap |= STA_CAP_HE; if (sta->deflink.he_6ghz_capa.capa) ra->af = le16_get_bits(sta->deflink.he_6ghz_capa.capa, IEEE80211_HE_6GHZ_CAP_MAX_AMPDU_LEN_EXP); } ra->sta_cap = cpu_to_le32(cap); } int mt7915_mcu_add_rate_ctrl(struct mt7915_dev *dev, struct ieee80211_vif *vif, struct ieee80211_sta *sta, bool changed) { struct mt7915_vif *mvif = (struct mt7915_vif *)vif->drv_priv; struct mt7915_sta *msta = (struct mt7915_sta *)sta->drv_priv; struct sk_buff *skb; int ret; skb = mt76_connac_mcu_alloc_sta_req(&dev->mt76, &mvif->mt76, &msta->wcid); if (IS_ERR(skb)) return PTR_ERR(skb); /* firmware rc algorithm refers to sta_rec_he for HE control. * once dev->rc_work changes the settings driver should also * update sta_rec_he here. */ if (changed) mt7915_mcu_sta_he_tlv(skb, sta, vif); /* sta_rec_ra accommodates BW, NSS and only MCS range format * i.e 0-{7,8,9} for VHT. */ mt7915_mcu_sta_rate_ctrl_tlv(skb, dev, vif, sta); ret = mt76_mcu_skb_send_msg(&dev->mt76, skb, MCU_EXT_CMD(STA_REC_UPDATE), true); if (ret) return ret; /* sta_rec_ra_fixed accommodates single rate, (HE)GI and HE_LTE, * and updates as peer fixed rate parameters, which overrides * sta_rec_ra and firmware rate control algorithm. */ return mt7915_mcu_add_rate_ctrl_fixed(dev, vif, sta); } static int mt7915_mcu_add_group(struct mt7915_dev *dev, struct ieee80211_vif *vif, struct ieee80211_sta *sta) { #define MT_STA_BSS_GROUP 1 struct mt7915_vif *mvif = (struct mt7915_vif *)vif->drv_priv; struct mt7915_sta *msta; struct { __le32 action; u8 wlan_idx_lo; u8 status; u8 wlan_idx_hi; u8 rsv0[5]; __le32 val; u8 rsv1[8]; } __packed req = { .action = cpu_to_le32(MT_STA_BSS_GROUP), .val = cpu_to_le32(mvif->mt76.idx % 16), }; msta = sta ? (struct mt7915_sta *)sta->drv_priv : &mvif->sta; req.wlan_idx_lo = to_wcid_lo(msta->wcid.idx); req.wlan_idx_hi = to_wcid_hi(msta->wcid.idx); return mt76_mcu_send_msg(&dev->mt76, MCU_EXT_CMD(SET_DRR_CTRL), &req, sizeof(req), true); } int mt7915_mcu_add_sta(struct mt7915_dev *dev, struct ieee80211_vif *vif, struct ieee80211_sta *sta, int conn_state, bool newly) { struct mt7915_vif *mvif = (struct mt7915_vif *)vif->drv_priv; struct ieee80211_link_sta *link_sta; struct mt7915_sta *msta; struct sk_buff *skb; int ret; msta = sta ? (struct mt7915_sta *)sta->drv_priv : &mvif->sta; link_sta = sta ? &sta->deflink : NULL; skb = mt76_connac_mcu_alloc_sta_req(&dev->mt76, &mvif->mt76, &msta->wcid); if (IS_ERR(skb)) return PTR_ERR(skb); /* starec basic */ mt76_connac_mcu_sta_basic_tlv(&dev->mt76, skb, &vif->bss_conf, link_sta, conn_state, newly); /* tag order is in accordance with firmware dependency. */ if (sta && conn_state != CONN_STATE_DISCONNECT) { /* starec bfer */ mt7915_mcu_sta_bfer_tlv(dev, skb, vif, sta); /* starec ht */ mt7915_mcu_sta_ht_tlv(skb, sta); /* starec vht */ mt7915_mcu_sta_vht_tlv(skb, sta); /* starec uapsd */ mt76_connac_mcu_sta_uapsd(skb, vif, sta); } if (newly || conn_state != CONN_STATE_DISCONNECT) { ret = mt7915_mcu_sta_wtbl_tlv(dev, skb, vif, sta); if (ret) { dev_kfree_skb(skb); return ret; } } if (conn_state == CONN_STATE_DISCONNECT) goto out; if (sta) { /* starec amsdu */ mt7915_mcu_sta_amsdu_tlv(dev, skb, vif, sta); /* starec he */ mt7915_mcu_sta_he_tlv(skb, sta, vif); /* starec muru */ mt7915_mcu_sta_muru_tlv(dev, skb, sta, vif); /* starec bfee */ mt7915_mcu_sta_bfee_tlv(dev, skb, vif, sta); } ret = mt7915_mcu_add_group(dev, vif, sta); if (ret) { dev_kfree_skb(skb); return ret; } out: ret = mt76_connac_mcu_sta_wed_update(&dev->mt76, skb); if (ret) return ret; return mt76_mcu_skb_send_msg(&dev->mt76, skb, MCU_EXT_CMD(STA_REC_UPDATE), true); } int mt7915_mcu_wed_enable_rx_stats(struct mt7915_dev *dev) { #ifdef CONFIG_NET_MEDIATEK_SOC_WED struct mtk_wed_device *wed = &dev->mt76.mmio.wed; struct { __le32 args[2]; } req = { .args[0] = cpu_to_le32(1), .args[1] = cpu_to_le32(6), }; return mtk_wed_device_update_msg(wed, MTK_WED_WO_CMD_RXCNT_CTRL, &req, sizeof(req)); #else return 0; #endif } int mt7915_mcu_add_dev_info(struct mt7915_phy *phy, struct ieee80211_vif *vif, bool enable) { struct mt7915_dev *dev = phy->dev; struct mt7915_vif *mvif = (struct mt7915_vif *)vif->drv_priv; struct { struct req_hdr { u8 omac_idx; u8 band_idx; __le16 tlv_num; u8 is_tlv_append; u8 rsv[3]; } __packed hdr; struct req_tlv { __le16 tag; __le16 len; u8 active; u8 band_idx; u8 omac_addr[ETH_ALEN]; } __packed tlv; } data = { .hdr = { .omac_idx = mvif->mt76.omac_idx, .band_idx = mvif->mt76.band_idx, .tlv_num = cpu_to_le16(1), .is_tlv_append = 1, }, .tlv = { .tag = cpu_to_le16(DEV_INFO_ACTIVE), .len = cpu_to_le16(sizeof(struct req_tlv)), .active = enable, .band_idx = mvif->mt76.band_idx, }, }; if (mvif->mt76.omac_idx >= REPEATER_BSSID_START) return mt7915_mcu_muar_config(phy, vif, false, enable); memcpy(data.tlv.omac_addr, vif->addr, ETH_ALEN); return mt76_mcu_send_msg(&dev->mt76, MCU_EXT_CMD(DEV_INFO_UPDATE), &data, sizeof(data), true); } static void mt7915_mcu_beacon_cntdwn(struct ieee80211_vif *vif, struct sk_buff *rskb, struct sk_buff *skb, struct bss_info_bcn *bcn, struct ieee80211_mutable_offsets *offs) { struct bss_info_bcn_cntdwn *info; struct tlv *tlv; int sub_tag; if (!offs->cntdwn_counter_offs[0]) return; sub_tag = vif->bss_conf.csa_active ? BSS_INFO_BCN_CSA : BSS_INFO_BCN_BCC; tlv = mt7915_mcu_add_nested_subtlv(rskb, sub_tag, sizeof(*info), &bcn->sub_ntlv, &bcn->len); info = (struct bss_info_bcn_cntdwn *)tlv; info->cnt = skb->data[offs->cntdwn_counter_offs[0]]; } static void mt7915_mcu_beacon_mbss(struct sk_buff *rskb, struct sk_buff *skb, struct ieee80211_vif *vif, struct bss_info_bcn *bcn, struct ieee80211_mutable_offsets *offs) { struct bss_info_bcn_mbss *mbss; const struct element *elem; struct tlv *tlv; if (!vif->bss_conf.bssid_indicator) return; tlv = mt7915_mcu_add_nested_subtlv(rskb, BSS_INFO_BCN_MBSSID, sizeof(*mbss), &bcn->sub_ntlv, &bcn->len); mbss = (struct bss_info_bcn_mbss *)tlv; mbss->offset[0] = cpu_to_le16(offs->tim_offset); mbss->bitmap = cpu_to_le32(1); for_each_element_id(elem, WLAN_EID_MULTIPLE_BSSID, &skb->data[offs->mbssid_off], skb->len - offs->mbssid_off) { const struct element *sub_elem; if (elem->datalen < 2) continue; for_each_element(sub_elem, elem->data + 1, elem->datalen - 1) { const struct ieee80211_bssid_index *idx; const u8 *idx_ie; if (sub_elem->id || sub_elem->datalen < 4) continue; /* not a valid BSS profile */ /* Find WLAN_EID_MULTI_BSSID_IDX * in the merged nontransmitted profile */ idx_ie = cfg80211_find_ie(WLAN_EID_MULTI_BSSID_IDX, sub_elem->data, sub_elem->datalen); if (!idx_ie || idx_ie[1] < sizeof(*idx)) continue; #if defined(__linux__) idx = (void *)(idx_ie + 2); #elif defined(__FreeBSD__) idx = (const void *)(idx_ie + 2); #endif if (!idx->bssid_index || idx->bssid_index > 31) continue; mbss->offset[idx->bssid_index] = cpu_to_le16(idx_ie - skb->data); mbss->bitmap |= cpu_to_le32(BIT(idx->bssid_index)); } } } static void mt7915_mcu_beacon_cont(struct mt7915_dev *dev, struct ieee80211_vif *vif, struct sk_buff *rskb, struct sk_buff *skb, struct bss_info_bcn *bcn, struct ieee80211_mutable_offsets *offs) { struct mt76_wcid *wcid = &dev->mt76.global_wcid; struct bss_info_bcn_cont *cont; struct tlv *tlv; u8 *buf; int len = sizeof(*cont) + MT_TXD_SIZE + skb->len; len = (len & 0x3) ? ((len | 0x3) + 1) : len; tlv = mt7915_mcu_add_nested_subtlv(rskb, BSS_INFO_BCN_CONTENT, len, &bcn->sub_ntlv, &bcn->len); cont = (struct bss_info_bcn_cont *)tlv; cont->pkt_len = cpu_to_le16(MT_TXD_SIZE + skb->len); cont->tim_ofs = cpu_to_le16(offs->tim_offset); if (offs->cntdwn_counter_offs[0]) { u16 offset = offs->cntdwn_counter_offs[0]; if (vif->bss_conf.csa_active) cont->csa_ofs = cpu_to_le16(offset - 4); if (vif->bss_conf.color_change_active) cont->bcc_ofs = cpu_to_le16(offset - 3); } buf = (u8 *)tlv + sizeof(*cont); mt7915_mac_write_txwi(&dev->mt76, (__le32 *)buf, skb, wcid, 0, NULL, 0, BSS_CHANGED_BEACON); memcpy(buf + MT_TXD_SIZE, skb->data, skb->len); } int mt7915_mcu_add_inband_discov(struct mt7915_dev *dev, struct ieee80211_vif *vif, u32 changed) { #define OFFLOAD_TX_MODE_SU BIT(0) #define OFFLOAD_TX_MODE_MU BIT(1) struct ieee80211_hw *hw = mt76_hw(dev); struct mt7915_phy *phy = mt7915_hw_phy(hw); struct mt7915_vif *mvif = (struct mt7915_vif *)vif->drv_priv; struct cfg80211_chan_def *chandef = &mvif->phy->mt76->chandef; enum nl80211_band band = chandef->chan->band; struct mt76_wcid *wcid = &dev->mt76.global_wcid; struct bss_info_bcn *bcn; struct bss_info_inband_discovery *discov; struct ieee80211_tx_info *info; struct sk_buff *rskb, *skb = NULL; struct tlv *tlv, *sub_tlv; bool ext_phy = phy != &dev->phy; u8 *buf, interval; int len; if (vif->bss_conf.nontransmitted) return 0; rskb = __mt76_connac_mcu_alloc_sta_req(&dev->mt76, &mvif->mt76, NULL, MT7915_MAX_BSS_OFFLOAD_SIZE); if (IS_ERR(rskb)) return PTR_ERR(rskb); tlv = mt76_connac_mcu_add_tlv(rskb, BSS_INFO_OFFLOAD, sizeof(*bcn)); bcn = (struct bss_info_bcn *)tlv; bcn->enable = true; if (changed & BSS_CHANGED_FILS_DISCOVERY) { interval = vif->bss_conf.fils_discovery.max_interval; skb = ieee80211_get_fils_discovery_tmpl(hw, vif); } else if (changed & BSS_CHANGED_UNSOL_BCAST_PROBE_RESP && vif->bss_conf.unsol_bcast_probe_resp_interval) { interval = vif->bss_conf.unsol_bcast_probe_resp_interval; skb = ieee80211_get_unsol_bcast_probe_resp_tmpl(hw, vif); } if (!skb) { dev_kfree_skb(rskb); return -EINVAL; } info = IEEE80211_SKB_CB(skb); info->control.vif = vif; info->band = band; info->hw_queue |= FIELD_PREP(MT_TX_HW_QUEUE_PHY, ext_phy); len = sizeof(*discov) + MT_TXD_SIZE + skb->len; len = (len & 0x3) ? ((len | 0x3) + 1) : len; if (skb->len > MT7915_MAX_BEACON_SIZE) { dev_err(dev->mt76.dev, "inband discovery size limit exceed\n"); dev_kfree_skb(rskb); dev_kfree_skb(skb); return -EINVAL; } sub_tlv = mt7915_mcu_add_nested_subtlv(rskb, BSS_INFO_BCN_DISCOV, len, &bcn->sub_ntlv, &bcn->len); discov = (struct bss_info_inband_discovery *)sub_tlv; discov->tx_mode = OFFLOAD_TX_MODE_SU; /* 0: UNSOL PROBE RESP, 1: FILS DISCOV */ discov->tx_type = !!(changed & BSS_CHANGED_FILS_DISCOVERY); discov->tx_interval = interval; discov->prob_rsp_len = cpu_to_le16(MT_TXD_SIZE + skb->len); discov->enable = !!interval; buf = (u8 *)sub_tlv + sizeof(*discov); mt7915_mac_write_txwi(&dev->mt76, (__le32 *)buf, skb, wcid, 0, NULL, 0, changed); memcpy(buf + MT_TXD_SIZE, skb->data, skb->len); dev_kfree_skb(skb); return mt76_mcu_skb_send_msg(&phy->dev->mt76, rskb, MCU_EXT_CMD(BSS_INFO_UPDATE), true); } int mt7915_mcu_add_beacon(struct ieee80211_hw *hw, struct ieee80211_vif *vif, int en, u32 changed) { struct mt7915_dev *dev = mt7915_hw_dev(hw); struct mt7915_phy *phy = mt7915_hw_phy(hw); struct mt7915_vif *mvif = (struct mt7915_vif *)vif->drv_priv; struct ieee80211_mutable_offsets offs; struct ieee80211_tx_info *info; struct sk_buff *skb, *rskb; struct tlv *tlv; struct bss_info_bcn *bcn; int len = MT7915_MAX_BSS_OFFLOAD_SIZE; bool ext_phy = phy != &dev->phy; if (vif->bss_conf.nontransmitted) return 0; rskb = __mt76_connac_mcu_alloc_sta_req(&dev->mt76, &mvif->mt76, NULL, len); if (IS_ERR(rskb)) return PTR_ERR(rskb); tlv = mt76_connac_mcu_add_tlv(rskb, BSS_INFO_OFFLOAD, sizeof(*bcn)); bcn = (struct bss_info_bcn *)tlv; bcn->enable = en; if (!en) goto out; skb = ieee80211_beacon_get_template(hw, vif, &offs, 0); if (!skb) { dev_kfree_skb(rskb); return -EINVAL; } if (skb->len > MT7915_MAX_BEACON_SIZE) { dev_err(dev->mt76.dev, "Bcn size limit exceed\n"); dev_kfree_skb(rskb); dev_kfree_skb(skb); return -EINVAL; } info = IEEE80211_SKB_CB(skb); info->hw_queue = FIELD_PREP(MT_TX_HW_QUEUE_PHY, ext_phy); mt7915_mcu_beacon_cntdwn(vif, rskb, skb, bcn, &offs); mt7915_mcu_beacon_mbss(rskb, skb, vif, bcn, &offs); mt7915_mcu_beacon_cont(dev, vif, rskb, skb, bcn, &offs); dev_kfree_skb(skb); out: return mt76_mcu_skb_send_msg(&phy->dev->mt76, rskb, MCU_EXT_CMD(BSS_INFO_UPDATE), true); } static int mt7915_driver_own(struct mt7915_dev *dev, u8 band) { mt76_wr(dev, MT_TOP_LPCR_HOST_BAND(band), MT_TOP_LPCR_HOST_DRV_OWN); if (!mt76_poll_msec(dev, MT_TOP_LPCR_HOST_BAND(band), MT_TOP_LPCR_HOST_FW_OWN_STAT, 0, 500)) { dev_err(dev->mt76.dev, "Timeout for driver own\n"); return -EIO; } /* clear irq when the driver own success */ mt76_wr(dev, MT_TOP_LPCR_HOST_BAND_IRQ_STAT(band), MT_TOP_LPCR_HOST_BAND_STAT); return 0; } static int mt7915_firmware_state(struct mt7915_dev *dev, bool wa) { u32 state = FIELD_PREP(MT_TOP_MISC_FW_STATE, wa ? FW_STATE_RDY : FW_STATE_FW_DOWNLOAD); if (!mt76_poll_msec(dev, MT_TOP_MISC, MT_TOP_MISC_FW_STATE, state, 1000)) { dev_err(dev->mt76.dev, "Timeout for initializing firmware\n"); return -EIO; } return 0; } static int mt7915_load_firmware(struct mt7915_dev *dev) { int ret; /* Release Semaphore if taken by previous failed attempt */ ret = mt76_connac_mcu_patch_sem_ctrl(&dev->mt76, false); if (ret != PATCH_REL_SEM_SUCCESS) { dev_err(dev->mt76.dev, "Could not release semaphore\n"); /* Continue anyways */ } /* Always restart MCU firmware */ mt76_connac_mcu_restart(&dev->mt76); /* Check if MCU is ready */ ret = mt7915_firmware_state(dev, false); if (ret) { dev_err(dev->mt76.dev, "Firmware did not enter download state\n"); return ret; } ret = mt76_connac2_load_patch(&dev->mt76, fw_name_var(dev, ROM_PATCH)); if (ret) return ret; ret = mt76_connac2_load_ram(&dev->mt76, fw_name_var(dev, FIRMWARE_WM), fw_name(dev, FIRMWARE_WA)); if (ret) return ret; ret = mt7915_firmware_state(dev, true); if (ret) return ret; mt76_queue_tx_cleanup(dev, dev->mt76.q_mcu[MT_MCUQ_FWDL], false); dev_dbg(dev->mt76.dev, "Firmware init done\n"); return 0; } int mt7915_mcu_fw_log_2_host(struct mt7915_dev *dev, u8 type, u8 ctrl) { struct { u8 ctrl_val; u8 pad[3]; } data = { .ctrl_val = ctrl }; if (type == MCU_FW_LOG_WA) return mt76_mcu_send_msg(&dev->mt76, MCU_WA_EXT_CMD(FW_LOG_2_HOST), &data, sizeof(data), true); return mt76_mcu_send_msg(&dev->mt76, MCU_EXT_CMD(FW_LOG_2_HOST), &data, sizeof(data), true); } int mt7915_mcu_fw_dbg_ctrl(struct mt7915_dev *dev, u32 module, u8 level) { struct { u8 ver; u8 pad; __le16 len; u8 level; u8 rsv[3]; __le32 module_idx; } data = { .module_idx = cpu_to_le32(module), .level = level, }; return mt76_mcu_send_msg(&dev->mt76, MCU_EXT_CMD(FW_DBG_CTRL), &data, sizeof(data), false); } int mt7915_mcu_muru_debug_set(struct mt7915_dev *dev, bool enabled) { struct { __le32 cmd; u8 enable; } data = { .cmd = cpu_to_le32(MURU_SET_TXC_TX_STATS_EN), .enable = enabled, }; return mt76_mcu_send_msg(&dev->mt76, MCU_EXT_CMD(MURU_CTRL), &data, sizeof(data), false); } int mt7915_mcu_muru_debug_get(struct mt7915_phy *phy) { struct mt7915_dev *dev = phy->dev; struct sk_buff *skb; struct mt7915_mcu_muru_stats *mu_stats; int ret; struct { __le32 cmd; u8 band_idx; } req = { .cmd = cpu_to_le32(MURU_GET_TXC_TX_STATS), .band_idx = phy->mt76->band_idx, }; ret = mt76_mcu_send_and_get_msg(&dev->mt76, MCU_EXT_CMD(MURU_CTRL), &req, sizeof(req), true, &skb); if (ret) return ret; mu_stats = (struct mt7915_mcu_muru_stats *)(skb->data); /* accumulate stats, these are clear-on-read */ #define __dl_u32(s) phy->mib.dl_##s += le32_to_cpu(mu_stats->dl.s) #define __ul_u32(s) phy->mib.ul_##s += le32_to_cpu(mu_stats->ul.s) __dl_u32(cck_cnt); __dl_u32(ofdm_cnt); __dl_u32(htmix_cnt); __dl_u32(htgf_cnt); __dl_u32(vht_su_cnt); __dl_u32(vht_2mu_cnt); __dl_u32(vht_3mu_cnt); __dl_u32(vht_4mu_cnt); __dl_u32(he_su_cnt); __dl_u32(he_2ru_cnt); __dl_u32(he_2mu_cnt); __dl_u32(he_3ru_cnt); __dl_u32(he_3mu_cnt); __dl_u32(he_4ru_cnt); __dl_u32(he_4mu_cnt); __dl_u32(he_5to8ru_cnt); __dl_u32(he_9to16ru_cnt); __dl_u32(he_gtr16ru_cnt); __ul_u32(hetrig_su_cnt); __ul_u32(hetrig_2ru_cnt); __ul_u32(hetrig_3ru_cnt); __ul_u32(hetrig_4ru_cnt); __ul_u32(hetrig_5to8ru_cnt); __ul_u32(hetrig_9to16ru_cnt); __ul_u32(hetrig_gtr16ru_cnt); __ul_u32(hetrig_2mu_cnt); __ul_u32(hetrig_3mu_cnt); __ul_u32(hetrig_4mu_cnt); #undef __dl_u32 #undef __ul_u32 dev_kfree_skb(skb); return 0; } static int mt7915_mcu_set_mwds(struct mt7915_dev *dev, bool enabled) { struct { u8 enable; u8 _rsv[3]; } __packed req = { .enable = enabled }; return mt76_mcu_send_msg(&dev->mt76, MCU_WA_EXT_CMD(MWDS_SUPPORT), &req, sizeof(req), false); } int mt7915_mcu_set_muru_ctrl(struct mt7915_dev *dev, u32 cmd, u32 val) { struct { __le32 cmd; u8 val[4]; } __packed req = { .cmd = cpu_to_le32(cmd), }; put_unaligned_le32(val, req.val); return mt76_mcu_send_msg(&dev->mt76, MCU_EXT_CMD(MURU_CTRL), &req, sizeof(req), false); } static int mt7915_mcu_init_rx_airtime(struct mt7915_dev *dev) { #define RX_AIRTIME_FEATURE_CTRL 1 #define RX_AIRTIME_BITWISE_CTRL 2 #define RX_AIRTIME_CLEAR_EN 1 struct { __le16 field; __le16 sub_field; __le32 set_status; __le32 get_status; u8 _rsv[12]; bool airtime_en; bool mibtime_en; bool earlyend_en; u8 _rsv1[9]; bool airtime_clear; bool mibtime_clear; u8 _rsv2[98]; } __packed req = { .field = cpu_to_le16(RX_AIRTIME_BITWISE_CTRL), .sub_field = cpu_to_le16(RX_AIRTIME_CLEAR_EN), .airtime_clear = true, }; int ret; ret = mt76_mcu_send_msg(&dev->mt76, MCU_EXT_CMD(RX_AIRTIME_CTRL), &req, sizeof(req), true); if (ret) return ret; req.field = cpu_to_le16(RX_AIRTIME_FEATURE_CTRL); req.sub_field = cpu_to_le16(RX_AIRTIME_CLEAR_EN); req.airtime_en = true; return mt76_mcu_send_msg(&dev->mt76, MCU_EXT_CMD(RX_AIRTIME_CTRL), &req, sizeof(req), true); } static int mt7915_red_set_watermark(struct mt7915_dev *dev) { #define RED_GLOBAL_TOKEN_WATERMARK 2 struct { __le32 args[3]; u8 cmd; u8 version; u8 __rsv1[4]; __le16 len; __le16 high_mark; __le16 low_mark; u8 __rsv2[12]; } __packed req = { .args[0] = cpu_to_le32(MCU_WA_PARAM_RED_SETTING), .cmd = RED_GLOBAL_TOKEN_WATERMARK, .len = cpu_to_le16(sizeof(req) - sizeof(req.args)), .high_mark = cpu_to_le16(MT7915_HW_TOKEN_SIZE - 256), .low_mark = cpu_to_le16(MT7915_HW_TOKEN_SIZE - 256 - 1536), }; return mt76_mcu_send_msg(&dev->mt76, MCU_WA_PARAM_CMD(SET), &req, sizeof(req), false); } static int mt7915_mcu_set_red(struct mt7915_dev *dev, bool enabled) { #define RED_DISABLE 0 #define RED_BY_WA_ENABLE 2 int ret; u32 red_type = enabled ? RED_BY_WA_ENABLE : RED_DISABLE; __le32 req = cpu_to_le32(red_type); if (enabled) { ret = mt7915_red_set_watermark(dev); if (ret < 0) return ret; } ret = mt76_mcu_send_msg(&dev->mt76, MCU_EXT_CMD(RED_ENABLE), &req, sizeof(req), false); if (ret < 0) return ret; return mt7915_mcu_wa_cmd(dev, MCU_WA_PARAM_CMD(SET), MCU_WA_PARAM_RED, enabled, 0); } int mt7915_mcu_init_firmware(struct mt7915_dev *dev) { int ret; /* force firmware operation mode into normal state, * which should be set before firmware download stage. */ mt76_wr(dev, MT_SWDEF_MODE, MT_SWDEF_NORMAL_MODE); ret = mt7915_driver_own(dev, 0); if (ret) return ret; /* set driver own for band1 when two hif exist */ if (dev->hif2) { ret = mt7915_driver_own(dev, 1); if (ret) return ret; } ret = mt7915_load_firmware(dev); if (ret) return ret; set_bit(MT76_STATE_MCU_RUNNING, &dev->mphy.state); ret = mt7915_mcu_fw_log_2_host(dev, MCU_FW_LOG_WM, 0); if (ret) return ret; ret = mt7915_mcu_fw_log_2_host(dev, MCU_FW_LOG_WA, 0); if (ret) return ret; mt76_connac_mcu_del_wtbl_all(&dev->mt76); if ((mtk_wed_device_active(&dev->mt76.mmio.wed) && is_mt7915(&dev->mt76)) || !mtk_wed_get_rx_capa(&dev->mt76.mmio.wed)) mt7915_mcu_wa_cmd(dev, MCU_WA_PARAM_CMD(CAPABILITY), 0, 0, 0); ret = mt7915_mcu_set_mwds(dev, 1); if (ret) return ret; ret = mt7915_mcu_set_muru_ctrl(dev, MURU_SET_PLATFORM_TYPE, MURU_PLATFORM_TYPE_PERF_LEVEL_2); if (ret) return ret; ret = mt7915_mcu_init_rx_airtime(dev); if (ret) return ret; return mt7915_mcu_set_red(dev, mtk_wed_device_active(&dev->mt76.mmio.wed)); } int mt7915_mcu_init(struct mt7915_dev *dev) { static const struct mt76_mcu_ops mt7915_mcu_ops = { .max_retry = 1, .headroom = sizeof(struct mt76_connac2_mcu_txd), .mcu_skb_prepare_msg = mt76_connac2_mcu_fill_message, .mcu_skb_send_msg = mt7915_mcu_send_message, .mcu_parse_response = mt7915_mcu_parse_response, }; dev->mt76.mcu_ops = &mt7915_mcu_ops; return mt7915_mcu_init_firmware(dev); } void mt7915_mcu_exit(struct mt7915_dev *dev) { mt76_connac_mcu_restart(&dev->mt76); if (mt7915_firmware_state(dev, false)) { dev_err(dev->mt76.dev, "Failed to exit mcu\n"); goto out; } mt76_wr(dev, MT_TOP_LPCR_HOST_BAND(0), MT_TOP_LPCR_HOST_FW_OWN); if (dev->hif2) mt76_wr(dev, MT_TOP_LPCR_HOST_BAND(1), MT_TOP_LPCR_HOST_FW_OWN); out: skb_queue_purge(&dev->mt76.mcu.res_q); } static int mt7915_mcu_set_rx_hdr_trans_blacklist(struct mt7915_dev *dev, int band) { struct { u8 operation; u8 count; u8 _rsv[2]; u8 index; u8 enable; __le16 etype; } req = { .operation = 1, .count = 1, .enable = 1, .etype = cpu_to_le16(ETH_P_PAE), }; return mt76_mcu_send_msg(&dev->mt76, MCU_EXT_CMD(RX_HDR_TRANS), &req, sizeof(req), false); } int mt7915_mcu_set_mac(struct mt7915_dev *dev, int band, bool enable, bool hdr_trans) { struct { u8 operation; u8 enable; u8 check_bssid; u8 insert_vlan; u8 remove_vlan; u8 tid; u8 mode; u8 rsv; } __packed req_trans = { .enable = hdr_trans, }; struct { u8 enable; u8 band; u8 rsv[2]; } __packed req_mac = { .enable = enable, .band = band, }; int ret; ret = mt76_mcu_send_msg(&dev->mt76, MCU_EXT_CMD(RX_HDR_TRANS), &req_trans, sizeof(req_trans), false); if (ret) return ret; if (hdr_trans) mt7915_mcu_set_rx_hdr_trans_blacklist(dev, band); return mt76_mcu_send_msg(&dev->mt76, MCU_EXT_CMD(MAC_INIT_CTRL), &req_mac, sizeof(req_mac), true); } int mt7915_mcu_update_edca(struct mt7915_dev *dev, void *param) { struct mt7915_mcu_tx *req = (struct mt7915_mcu_tx *)param; u8 num = req->total; size_t len = sizeof(*req) - (IEEE80211_NUM_ACS - num) * sizeof(struct edca); return mt76_mcu_send_msg(&dev->mt76, MCU_EXT_CMD(EDCA_UPDATE), req, len, true); } int mt7915_mcu_set_tx(struct mt7915_dev *dev, struct ieee80211_vif *vif) { #define TX_CMD_MODE 1 struct mt7915_mcu_tx req = { .valid = true, .mode = TX_CMD_MODE, .total = IEEE80211_NUM_ACS, }; struct mt7915_vif *mvif = (struct mt7915_vif *)vif->drv_priv; int ac; for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) { struct ieee80211_tx_queue_params *q = &mvif->queue_params[ac]; struct edca *e = &req.edca[ac]; e->set = WMM_PARAM_SET; e->queue = ac + mvif->mt76.wmm_idx * MT76_CONNAC_MAX_WMM_SETS; e->aifs = q->aifs; e->txop = cpu_to_le16(q->txop); if (q->cw_min) e->cw_min = fls(q->cw_min); else e->cw_min = 5; if (q->cw_max) e->cw_max = cpu_to_le16(fls(q->cw_max)); else e->cw_max = cpu_to_le16(10); } return mt7915_mcu_update_edca(dev, &req); } int mt7915_mcu_set_fcc5_lpn(struct mt7915_dev *dev, int val) { struct { __le32 tag; __le16 min_lpn; u8 rsv[2]; } __packed req = { .tag = cpu_to_le32(0x1), .min_lpn = cpu_to_le16(val), }; return mt76_mcu_send_msg(&dev->mt76, MCU_EXT_CMD(SET_RDD_TH), &req, sizeof(req), true); } int mt7915_mcu_set_pulse_th(struct mt7915_dev *dev, const struct mt7915_dfs_pulse *pulse) { struct { __le32 tag; __le32 max_width; /* us */ __le32 max_pwr; /* dbm */ __le32 min_pwr; /* dbm */ __le32 min_stgr_pri; /* us */ __le32 max_stgr_pri; /* us */ __le32 min_cr_pri; /* us */ __le32 max_cr_pri; /* us */ } __packed req = { .tag = cpu_to_le32(0x3), #define __req_field(field) .field = cpu_to_le32(pulse->field) __req_field(max_width), __req_field(max_pwr), __req_field(min_pwr), __req_field(min_stgr_pri), __req_field(max_stgr_pri), __req_field(min_cr_pri), __req_field(max_cr_pri), #undef __req_field }; return mt76_mcu_send_msg(&dev->mt76, MCU_EXT_CMD(SET_RDD_TH), &req, sizeof(req), true); } int mt7915_mcu_set_radar_th(struct mt7915_dev *dev, int index, const struct mt7915_dfs_pattern *pattern) { struct { __le32 tag; __le16 radar_type; u8 enb; u8 stgr; u8 min_crpn; u8 max_crpn; u8 min_crpr; u8 min_pw; __le32 min_pri; __le32 max_pri; u8 max_pw; u8 min_crbn; u8 max_crbn; u8 min_stgpn; u8 max_stgpn; u8 min_stgpr; u8 rsv[2]; __le32 min_stgpr_diff; } __packed req = { .tag = cpu_to_le32(0x2), .radar_type = cpu_to_le16(index), #define __req_field_u8(field) .field = pattern->field #define __req_field_u32(field) .field = cpu_to_le32(pattern->field) __req_field_u8(enb), __req_field_u8(stgr), __req_field_u8(min_crpn), __req_field_u8(max_crpn), __req_field_u8(min_crpr), __req_field_u8(min_pw), __req_field_u32(min_pri), __req_field_u32(max_pri), __req_field_u8(max_pw), __req_field_u8(min_crbn), __req_field_u8(max_crbn), __req_field_u8(min_stgpn), __req_field_u8(max_stgpn), __req_field_u8(min_stgpr), __req_field_u32(min_stgpr_diff), #undef __req_field_u8 #undef __req_field_u32 }; return mt76_mcu_send_msg(&dev->mt76, MCU_EXT_CMD(SET_RDD_TH), &req, sizeof(req), true); } static int mt7915_mcu_background_chain_ctrl(struct mt7915_phy *phy, struct cfg80211_chan_def *chandef, int cmd) { struct mt7915_dev *dev = phy->dev; struct mt76_phy *mphy = phy->mt76; struct ieee80211_channel *chan = mphy->chandef.chan; int freq = mphy->chandef.center_freq1; struct mt7915_mcu_background_chain_ctrl req = { .monitor_scan_type = 2, /* simple rx */ }; if (!chandef && cmd != CH_SWITCH_BACKGROUND_SCAN_STOP) return -EINVAL; if (!cfg80211_chandef_valid(&mphy->chandef)) return -EINVAL; switch (cmd) { case CH_SWITCH_BACKGROUND_SCAN_START: { req.chan = chan->hw_value; req.central_chan = ieee80211_frequency_to_channel(freq); req.bw = mt76_connac_chan_bw(&mphy->chandef); req.monitor_chan = chandef->chan->hw_value; req.monitor_central_chan = ieee80211_frequency_to_channel(chandef->center_freq1); req.monitor_bw = mt76_connac_chan_bw(chandef); req.band_idx = phy->mt76->band_idx; req.scan_mode = 1; break; } case CH_SWITCH_BACKGROUND_SCAN_RUNNING: req.monitor_chan = chandef->chan->hw_value; req.monitor_central_chan = ieee80211_frequency_to_channel(chandef->center_freq1); req.band_idx = phy->mt76->band_idx; req.scan_mode = 2; break; case CH_SWITCH_BACKGROUND_SCAN_STOP: req.chan = chan->hw_value; req.central_chan = ieee80211_frequency_to_channel(freq); req.bw = mt76_connac_chan_bw(&mphy->chandef); req.tx_stream = hweight8(mphy->antenna_mask); req.rx_stream = mphy->antenna_mask; break; default: return -EINVAL; } req.band = chandef ? chandef->chan->band == NL80211_BAND_5GHZ : 1; return mt76_mcu_send_msg(&dev->mt76, MCU_EXT_CMD(OFFCH_SCAN_CTRL), &req, sizeof(req), false); } int mt7915_mcu_rdd_background_enable(struct mt7915_phy *phy, struct cfg80211_chan_def *chandef) { struct mt7915_dev *dev = phy->dev; int err, region, rdd_idx; rdd_idx = mt7915_get_rdd_idx(phy, true); if (rdd_idx < 0) return -EINVAL; if (!chandef) { /* disable offchain */ err = mt76_connac_mcu_rdd_cmd(&dev->mt76, RDD_STOP, rdd_idx, 0, 0); if (err) return err; return mt7915_mcu_background_chain_ctrl(phy, NULL, CH_SWITCH_BACKGROUND_SCAN_STOP); } err = mt7915_mcu_background_chain_ctrl(phy, chandef, CH_SWITCH_BACKGROUND_SCAN_START); if (err) return err; switch (dev->mt76.region) { case NL80211_DFS_ETSI: region = 0; break; case NL80211_DFS_JP: region = 2; break; case NL80211_DFS_FCC: default: region = 1; break; } return mt76_connac_mcu_rdd_cmd(&dev->mt76, RDD_START, rdd_idx, 0, region); } int mt7915_mcu_set_chan_info(struct mt7915_phy *phy, int cmd) { static const u8 ch_band[] = { [NL80211_BAND_2GHZ] = 0, [NL80211_BAND_5GHZ] = 1, [NL80211_BAND_6GHZ] = 2, }; struct mt7915_dev *dev = phy->dev; struct cfg80211_chan_def *chandef = &phy->mt76->chandef; int freq1 = chandef->center_freq1; u8 band = phy->mt76->band_idx; struct { u8 control_ch; u8 center_ch; u8 bw; u8 tx_path_num; u8 rx_path; /* mask or num */ u8 switch_reason; u8 band_idx; u8 center_ch2; /* for 80+80 only */ __le16 cac_case; u8 channel_band; u8 rsv0; __le32 outband_freq; u8 txpower_drop; u8 ap_bw; u8 ap_center_ch; u8 rsv1[57]; } __packed req = { .control_ch = chandef->chan->hw_value, .center_ch = ieee80211_frequency_to_channel(freq1), .bw = mt76_connac_chan_bw(chandef), .tx_path_num = hweight16(phy->mt76->chainmask), .rx_path = phy->mt76->chainmask >> (dev->chainshift * band), .band_idx = band, .channel_band = ch_band[chandef->chan->band], }; #ifdef CONFIG_NL80211_TESTMODE if (phy->mt76->test.tx_antenna_mask && mt76_testmode_enabled(phy->mt76)) { req.tx_path_num = fls(phy->mt76->test.tx_antenna_mask); req.rx_path = phy->mt76->test.tx_antenna_mask; } #endif if (mt76_connac_spe_idx(phy->mt76->antenna_mask)) req.tx_path_num = fls(phy->mt76->antenna_mask); if (phy->mt76->hw->conf.flags & IEEE80211_CONF_MONITOR) req.switch_reason = CH_SWITCH_NORMAL; else if (phy->mt76->offchannel || phy->mt76->hw->conf.flags & IEEE80211_CONF_IDLE) req.switch_reason = CH_SWITCH_SCAN_BYPASS_DPD; else if (!cfg80211_reg_can_beacon(phy->mt76->hw->wiphy, chandef, NL80211_IFTYPE_AP)) req.switch_reason = CH_SWITCH_DFS; else req.switch_reason = CH_SWITCH_NORMAL; if (cmd == MCU_EXT_CMD(CHANNEL_SWITCH)) req.rx_path = hweight8(req.rx_path); if (chandef->width == NL80211_CHAN_WIDTH_80P80) { int freq2 = chandef->center_freq2; req.center_ch2 = ieee80211_frequency_to_channel(freq2); } return mt76_mcu_send_msg(&dev->mt76, cmd, &req, sizeof(req), true); } static int mt7915_mcu_set_eeprom_flash(struct mt7915_dev *dev) { #define MAX_PAGE_IDX_MASK GENMASK(7, 5) #define PAGE_IDX_MASK GENMASK(4, 2) #define PER_PAGE_SIZE 0x400 struct mt7915_mcu_eeprom req = { .buffer_mode = EE_MODE_BUFFER }; u16 eeprom_size = mt7915_eeprom_size(dev); u8 total = DIV_ROUND_UP(eeprom_size, PER_PAGE_SIZE); u8 *eep = (u8 *)dev->mt76.eeprom.data; int eep_len; int i; for (i = 0; i < total; i++, eep += eep_len) { struct sk_buff *skb; int ret; if (i == total - 1 && !!(eeprom_size % PER_PAGE_SIZE)) eep_len = eeprom_size % PER_PAGE_SIZE; else eep_len = PER_PAGE_SIZE; skb = mt76_mcu_msg_alloc(&dev->mt76, NULL, sizeof(req) + eep_len); if (!skb) return -ENOMEM; req.format = FIELD_PREP(MAX_PAGE_IDX_MASK, total - 1) | FIELD_PREP(PAGE_IDX_MASK, i) | EE_FORMAT_WHOLE; req.len = cpu_to_le16(eep_len); skb_put_data(skb, &req, sizeof(req)); skb_put_data(skb, eep, eep_len); ret = mt76_mcu_skb_send_msg(&dev->mt76, skb, MCU_EXT_CMD(EFUSE_BUFFER_MODE), true); if (ret) return ret; } return 0; } int mt7915_mcu_set_eeprom(struct mt7915_dev *dev) { struct mt7915_mcu_eeprom req = { .buffer_mode = EE_MODE_EFUSE, .format = EE_FORMAT_WHOLE, }; if (dev->flash_mode) return mt7915_mcu_set_eeprom_flash(dev); return mt76_mcu_send_msg(&dev->mt76, MCU_EXT_CMD(EFUSE_BUFFER_MODE), &req, sizeof(req), true); } int mt7915_mcu_get_eeprom(struct mt7915_dev *dev, u32 offset, u8 *read_buf) { struct mt7915_mcu_eeprom_info req = { .addr = cpu_to_le32(round_down(offset, MT7915_EEPROM_BLOCK_SIZE)), }; struct mt7915_mcu_eeprom_info *res; struct sk_buff *skb; u8 *buf = read_buf; int ret; ret = mt76_mcu_send_and_get_msg(&dev->mt76, MCU_EXT_QUERY(EFUSE_ACCESS), &req, sizeof(req), true, &skb); if (ret) return ret; res = (struct mt7915_mcu_eeprom_info *)skb->data; if (!buf) #if defined(__linux__) buf = dev->mt76.eeprom.data + le32_to_cpu(res->addr); #elif defined(__FreeBSD__) buf = (u8 *)dev->mt76.eeprom.data + le32_to_cpu(res->addr); #endif memcpy(buf, res->data, MT7915_EEPROM_BLOCK_SIZE); dev_kfree_skb(skb); return 0; } int mt7915_mcu_get_eeprom_free_block(struct mt7915_dev *dev, u8 *block_num) { struct { u8 _rsv; u8 version; u8 die_idx; u8 _rsv2; } __packed req = { .version = 1, }; struct sk_buff *skb; int ret; ret = mt76_mcu_send_and_get_msg(&dev->mt76, MCU_EXT_QUERY(EFUSE_FREE_BLOCK), &req, sizeof(req), true, &skb); if (ret) return ret; *block_num = *(u8 *)skb->data; dev_kfree_skb(skb); return 0; } static int mt7915_mcu_set_pre_cal(struct mt7915_dev *dev, u8 idx, u8 *data, u32 len, int cmd) { struct { u8 dir; u8 valid; __le16 bitmap; s8 precal; u8 action; u8 band; u8 idx; u8 rsv[4]; __le32 len; } req = {}; struct sk_buff *skb; skb = mt76_mcu_msg_alloc(&dev->mt76, NULL, sizeof(req) + len); if (!skb) return -ENOMEM; req.idx = idx; req.len = cpu_to_le32(len); skb_put_data(skb, &req, sizeof(req)); skb_put_data(skb, data, len); return mt76_mcu_skb_send_msg(&dev->mt76, skb, cmd, false); } int mt7915_mcu_apply_group_cal(struct mt7915_dev *dev) { u8 idx = 0, *cal = dev->cal, *eep = dev->mt76.eeprom.data; u32 total = mt7915_get_cal_group_size(dev); u32 offs = is_mt7915(&dev->mt76) ? MT_EE_DO_PRE_CAL : MT_EE_DO_PRE_CAL_V2; if (!(eep[offs] & MT_EE_WIFI_CAL_GROUP)) return 0; /* * Items: Rx DCOC, RSSI DCOC, Tx TSSI DCOC, Tx LPFG * Tx FDIQ, Tx DCIQ, Rx FDIQ, Rx FIIQ, ADCDCOC */ while (total > 0) { int ret, len; len = min_t(u32, total, MT_EE_CAL_UNIT); ret = mt7915_mcu_set_pre_cal(dev, idx, cal, len, MCU_EXT_CMD(GROUP_PRE_CAL_INFO)); if (ret) return ret; total -= len; cal += len; idx++; } return 0; } static int mt7915_find_freq_idx(const u16 *freqs, int n_freqs, u16 cur) { int i; for (i = 0; i < n_freqs; i++) if (cur == freqs[i]) return i; return -1; } static int mt7915_dpd_freq_idx(struct mt7915_dev *dev, u16 freq, u8 bw) { static const u16 freq_list_v1[] = { 5180, 5200, 5220, 5240, 5260, 5280, 5300, 5320, 5500, 5520, 5540, 5560, 5580, 5600, 5620, 5640, 5660, 5680, 5700, 5745, 5765, 5785, 5805, 5825 }; static const u16 freq_list_v2[] = { /* 6G BW20*/ 5955, 5975, 5995, 6015, 6035, 6055, 6075, 6095, 6115, 6135, 6155, 6175, 6195, 6215, 6235, 6255, 6275, 6295, 6315, 6335, 6355, 6375, 6395, 6415, 6435, 6455, 6475, 6495, 6515, 6535, 6555, 6575, 6595, 6615, 6635, 6655, 6675, 6695, 6715, 6735, 6755, 6775, 6795, 6815, 6835, 6855, 6875, 6895, 6915, 6935, 6955, 6975, 6995, 7015, 7035, 7055, 7075, 7095, 7115, /* 6G BW160 */ 6025, 6185, 6345, 6505, 6665, 6825, 6985, /* 5G BW20 */ 5180, 5200, 5220, 5240, 5260, 5280, 5300, 5320, 5500, 5520, 5540, 5560, 5580, 5600, 5620, 5640, 5660, 5680, 5700, 5720, 5745, 5765, 5785, 5805, 5825, 5845, 5865, 5885, /* 5G BW160 */ 5250, 5570, 5815 }; static const u16 freq_list_v2_7981[] = { /* 5G BW20 */ 5180, 5200, 5220, 5240, 5260, 5280, 5300, 5320, 5500, 5520, 5540, 5560, 5580, 5600, 5620, 5640, 5660, 5680, 5700, 5720, 5745, 5765, 5785, 5805, 5825, 5845, 5865, 5885, /* 5G BW160 */ 5250, 5570, 5815 }; const u16 *freq_list = freq_list_v1; int n_freqs = ARRAY_SIZE(freq_list_v1); int idx; if (!is_mt7915(&dev->mt76)) { if (is_mt7981(&dev->mt76)) { freq_list = freq_list_v2_7981; n_freqs = ARRAY_SIZE(freq_list_v2_7981); } else { freq_list = freq_list_v2; n_freqs = ARRAY_SIZE(freq_list_v2); } } if (freq < 4000) { if (freq < 2432) return n_freqs; if (freq < 2457) return n_freqs + 1; return n_freqs + 2; } if (bw == NL80211_CHAN_WIDTH_80P80) return -1; if (bw != NL80211_CHAN_WIDTH_20) { idx = mt7915_find_freq_idx(freq_list, n_freqs, freq + 10); if (idx >= 0) return idx; idx = mt7915_find_freq_idx(freq_list, n_freqs, freq - 10); if (idx >= 0) return idx; } return mt7915_find_freq_idx(freq_list, n_freqs, freq); } int mt7915_mcu_apply_tx_dpd(struct mt7915_phy *phy) { struct mt7915_dev *dev = phy->dev; struct cfg80211_chan_def *chandef = &phy->mt76->chandef; enum nl80211_band band = chandef->chan->band; u32 offs = is_mt7915(&dev->mt76) ? MT_EE_DO_PRE_CAL : MT_EE_DO_PRE_CAL_V2; u16 center_freq = chandef->center_freq1; u8 *cal = dev->cal, *eep = dev->mt76.eeprom.data; u8 dpd_mask, cal_num = is_mt7915(&dev->mt76) ? 2 : 3; int idx; switch (band) { case NL80211_BAND_2GHZ: dpd_mask = MT_EE_WIFI_CAL_DPD_2G; break; case NL80211_BAND_5GHZ: dpd_mask = MT_EE_WIFI_CAL_DPD_5G; break; case NL80211_BAND_6GHZ: dpd_mask = MT_EE_WIFI_CAL_DPD_6G; break; default: dpd_mask = 0; break; } if (!(eep[offs] & dpd_mask)) return 0; idx = mt7915_dpd_freq_idx(dev, center_freq, chandef->width); if (idx < 0) return -EINVAL; /* Items: Tx DPD, Tx Flatness */ idx = idx * cal_num; cal += mt7915_get_cal_group_size(dev) + (idx * MT_EE_CAL_UNIT); while (cal_num--) { int ret; ret = mt7915_mcu_set_pre_cal(dev, idx, cal, MT_EE_CAL_UNIT, MCU_EXT_CMD(DPD_PRE_CAL_INFO)); if (ret) return ret; idx++; cal += MT_EE_CAL_UNIT; } return 0; } int mt7915_mcu_get_chan_mib_info(struct mt7915_phy *phy, bool chan_switch) { struct mt76_channel_state *state = phy->mt76->chan_state; struct mt76_channel_state *state_ts = &phy->state_ts; struct mt7915_dev *dev = phy->dev; struct mt7915_mcu_mib *res, req[5]; struct sk_buff *skb; static const u32 *offs; int i, ret, len, offs_cc; u64 cc_tx; /* strict order */ if (is_mt7915(&dev->mt76)) { static const u32 chip_offs[] = { MIB_NON_WIFI_TIME, MIB_TX_TIME, MIB_RX_TIME, MIB_OBSS_AIRTIME, MIB_TXOP_INIT_COUNT, }; len = ARRAY_SIZE(chip_offs); offs = chip_offs; offs_cc = 20; } else { static const u32 chip_offs[] = { MIB_NON_WIFI_TIME_V2, MIB_TX_TIME_V2, MIB_RX_TIME_V2, MIB_OBSS_AIRTIME_V2 }; len = ARRAY_SIZE(chip_offs); offs = chip_offs; offs_cc = 0; } for (i = 0; i < len; i++) { req[i].band = cpu_to_le32(phy->mt76->band_idx); req[i].offs = cpu_to_le32(offs[i]); } ret = mt76_mcu_send_and_get_msg(&dev->mt76, MCU_EXT_CMD(GET_MIB_INFO), req, len * sizeof(req[0]), true, &skb); if (ret) return ret; res = (struct mt7915_mcu_mib *)(skb->data + offs_cc); #define __res_u64(s) le64_to_cpu(res[s].data) /* subtract Tx backoff time from Tx duration for MT7915 */ if (is_mt7915(&dev->mt76)) { u64 backoff = (__res_u64(4) & 0xffff) * 79; /* 16us + 9us * 7 */ cc_tx = __res_u64(1) - backoff; } else { cc_tx = __res_u64(1); } if (chan_switch) goto out; state->cc_tx += cc_tx - state_ts->cc_tx; state->cc_bss_rx += __res_u64(2) - state_ts->cc_bss_rx; state->cc_rx += __res_u64(2) + __res_u64(3) - state_ts->cc_rx; state->cc_busy += __res_u64(0) + cc_tx + __res_u64(2) + __res_u64(3) - state_ts->cc_busy; out: state_ts->cc_tx = cc_tx; state_ts->cc_bss_rx = __res_u64(2); state_ts->cc_rx = __res_u64(2) + __res_u64(3); state_ts->cc_busy = __res_u64(0) + cc_tx + __res_u64(2) + __res_u64(3); #undef __res_u64 dev_kfree_skb(skb); return 0; } int mt7915_mcu_get_temperature(struct mt7915_phy *phy) { struct mt7915_dev *dev = phy->dev; struct { u8 ctrl_id; u8 action; u8 band_idx; u8 rsv[5]; } req = { .ctrl_id = THERMAL_SENSOR_TEMP_QUERY, .band_idx = phy->mt76->band_idx, }; return mt76_mcu_send_msg(&dev->mt76, MCU_EXT_CMD(THERMAL_CTRL), &req, sizeof(req), true); } int mt7915_mcu_set_thermal_throttling(struct mt7915_phy *phy, u8 state) { struct mt7915_dev *dev = phy->dev; struct mt7915_mcu_thermal_ctrl req = { .band_idx = phy->mt76->band_idx, .ctrl_id = THERMAL_PROTECT_DUTY_CONFIG, }; int level, ret; /* set duty cycle and level */ for (level = 0; level < 4; level++) { req.duty.duty_level = level; req.duty.duty_cycle = state; state /= 2; ret = mt76_mcu_send_msg(&dev->mt76, MCU_EXT_CMD(THERMAL_PROT), &req, sizeof(req), false); if (ret) return ret; } return 0; } int mt7915_mcu_set_thermal_protect(struct mt7915_phy *phy) { struct mt7915_dev *dev = phy->dev; struct { struct mt7915_mcu_thermal_ctrl ctrl; __le32 trigger_temp; __le32 restore_temp; __le16 sustain_time; u8 rsv[2]; } __packed req = { .ctrl = { .band_idx = phy->mt76->band_idx, .type.protect_type = 1, .type.trigger_type = 1, }, }; int ret; req.ctrl.ctrl_id = THERMAL_PROTECT_DISABLE; ret = mt76_mcu_send_msg(&dev->mt76, MCU_EXT_CMD(THERMAL_PROT), &req, sizeof(req.ctrl), false); if (ret) return ret; /* set high-temperature trigger threshold */ req.ctrl.ctrl_id = THERMAL_PROTECT_ENABLE; /* add a safety margin ~10 */ req.restore_temp = cpu_to_le32(phy->throttle_temp[0] - 10); req.trigger_temp = cpu_to_le32(phy->throttle_temp[1]); req.sustain_time = cpu_to_le16(10); return mt76_mcu_send_msg(&dev->mt76, MCU_EXT_CMD(THERMAL_PROT), &req, sizeof(req), false); } int mt7915_mcu_set_txpower_frame_min(struct mt7915_phy *phy, s8 txpower) { struct mt7915_dev *dev = phy->dev; struct { u8 format_id; u8 rsv; u8 band_idx; s8 txpower_min; } __packed req = { .format_id = TX_POWER_LIMIT_FRAME_MIN, .band_idx = phy->mt76->band_idx, .txpower_min = txpower * 2, /* 0.5db */ }; return mt76_mcu_send_msg(&dev->mt76, MCU_EXT_CMD(TX_POWER_FEATURE_CTRL), &req, sizeof(req), true); } int mt7915_mcu_set_txpower_frame(struct mt7915_phy *phy, struct ieee80211_vif *vif, struct ieee80211_sta *sta, s8 txpower) { struct mt7915_sta *msta = (struct mt7915_sta *)sta->drv_priv; struct mt7915_dev *dev = phy->dev; struct mt76_phy *mphy = phy->mt76; struct { u8 format_id; u8 rsv[3]; u8 band_idx; s8 txpower_max; __le16 wcid; s8 txpower_offs[48]; } __packed req = { .format_id = TX_POWER_LIMIT_FRAME, .band_idx = phy->mt76->band_idx, .txpower_max = DIV_ROUND_UP(mphy->txpower_cur, 2), .wcid = cpu_to_le16(msta->wcid.idx), }; int ret; s8 txpower_sku[MT7915_SKU_RATE_NUM]; ret = mt7915_mcu_get_txpower_sku(phy, txpower_sku, sizeof(txpower_sku)); if (ret) return ret; txpower = mt76_get_power_bound(mphy, txpower); if (txpower > mphy->txpower_cur || txpower < 0) return -EINVAL; if (txpower) { u32 offs, len, i; if (sta->deflink.ht_cap.ht_supported) { const u8 *sku_len = mt7915_sku_group_len; offs = sku_len[SKU_CCK] + sku_len[SKU_OFDM]; len = sku_len[SKU_HT_BW20] + sku_len[SKU_HT_BW40]; if (sta->deflink.vht_cap.vht_supported) { offs += len; len = sku_len[SKU_VHT_BW20] * 4; if (sta->deflink.he_cap.has_he) { offs += len + sku_len[SKU_HE_RU26] * 3; len = sku_len[SKU_HE_RU242] * 4; } } } else { return -EINVAL; } for (i = 0; i < len; i++, offs++) req.txpower_offs[i] = DIV_ROUND_UP(txpower - txpower_sku[offs], 2); } return mt76_mcu_send_msg(&dev->mt76, MCU_EXT_CMD(TX_POWER_FEATURE_CTRL), &req, sizeof(req), true); } int mt7915_mcu_set_txpower_sku(struct mt7915_phy *phy) { struct mt7915_dev *dev = phy->dev; struct mt76_phy *mphy = phy->mt76; struct ieee80211_hw *hw = mphy->hw; struct mt7915_mcu_txpower_sku req = { .format_id = TX_POWER_LIMIT_TABLE, .band_idx = phy->mt76->band_idx, }; struct mt76_power_limits limits_array; s8 *la = (s8 *)&limits_array; int i, idx; int tx_power; tx_power = mt76_get_power_bound(mphy, hw->conf.power_level); tx_power = mt76_get_rate_power_limits(mphy, mphy->chandef.chan, &limits_array, tx_power); mphy->txpower_cur = tx_power; for (i = 0, idx = 0; i < ARRAY_SIZE(mt7915_sku_group_len); i++) { u8 mcs_num, len = mt7915_sku_group_len[i]; int j; if (i >= SKU_HT_BW20 && i <= SKU_VHT_BW160) { mcs_num = 10; if (i == SKU_HT_BW20 || i == SKU_VHT_BW20) la = (s8 *)&limits_array + 12; } else { mcs_num = len; } for (j = 0; j < min_t(u8, mcs_num, len); j++) req.txpower_sku[idx + j] = la[j]; la += mcs_num; idx += len; } return mt76_mcu_send_msg(&dev->mt76, MCU_EXT_CMD(TX_POWER_FEATURE_CTRL), &req, sizeof(req), true); } int mt7915_mcu_get_txpower_sku(struct mt7915_phy *phy, s8 *txpower, int len) { #define RATE_POWER_INFO 2 struct mt7915_dev *dev = phy->dev; struct { u8 format_id; u8 category; u8 band_idx; u8 _rsv; } __packed req = { .format_id = TX_POWER_LIMIT_INFO, .category = RATE_POWER_INFO, .band_idx = phy->mt76->band_idx, }; s8 txpower_sku[MT7915_SKU_RATE_NUM][2]; struct sk_buff *skb; int ret, i; ret = mt76_mcu_send_and_get_msg(&dev->mt76, MCU_EXT_CMD(TX_POWER_FEATURE_CTRL), &req, sizeof(req), true, &skb); if (ret) return ret; memcpy(txpower_sku, skb->data + 4, sizeof(txpower_sku)); for (i = 0; i < len; i++) txpower[i] = txpower_sku[i][req.band_idx]; dev_kfree_skb(skb); return 0; } int mt7915_mcu_set_test_param(struct mt7915_dev *dev, u8 param, bool test_mode, u8 en) { struct { u8 test_mode_en; u8 param_idx; u8 _rsv[2]; u8 enable; u8 _rsv2[3]; u8 pad[8]; } __packed req = { .test_mode_en = test_mode, .param_idx = param, .enable = en, }; return mt76_mcu_send_msg(&dev->mt76, MCU_EXT_CMD(ATE_CTRL), &req, sizeof(req), false); } int mt7915_mcu_set_sku_en(struct mt7915_phy *phy, bool enable) { struct mt7915_dev *dev = phy->dev; struct mt7915_sku { u8 format_id; u8 sku_enable; u8 band_idx; u8 rsv; } __packed req = { .format_id = TX_POWER_LIMIT_ENABLE, .band_idx = phy->mt76->band_idx, .sku_enable = enable, }; return mt76_mcu_send_msg(&dev->mt76, MCU_EXT_CMD(TX_POWER_FEATURE_CTRL), &req, sizeof(req), true); } int mt7915_mcu_set_ser(struct mt7915_dev *dev, u8 action, u8 set, u8 band) { struct { u8 action; u8 set; u8 band; u8 rsv; } req = { .action = action, .set = set, .band = band, }; return mt76_mcu_send_msg(&dev->mt76, MCU_EXT_CMD(SET_SER_TRIGGER), &req, sizeof(req), false); } int mt7915_mcu_set_txbf(struct mt7915_dev *dev, u8 action) { struct { u8 action; union { struct { u8 snd_mode; u8 sta_num; u8 rsv; u8 wlan_idx[4]; __le32 snd_period; /* ms */ } __packed snd; struct { bool ebf; bool ibf; u8 rsv; } __packed type; struct { u8 bf_num; u8 bf_bitmap; u8 bf_sel[8]; u8 rsv[5]; } __packed mod; }; } __packed req = { .action = action, }; #define MT_BF_PROCESSING 4 switch (action) { case MT_BF_SOUNDING_ON: req.snd.snd_mode = MT_BF_PROCESSING; break; case MT_BF_TYPE_UPDATE: req.type.ebf = true; req.type.ibf = dev->ibf; break; case MT_BF_MODULE_UPDATE: req.mod.bf_num = 2; req.mod.bf_bitmap = GENMASK(1, 0); break; default: return -EINVAL; } return mt76_mcu_send_msg(&dev->mt76, MCU_EXT_CMD(TXBF_ACTION), &req, sizeof(req), true); } static int mt7915_mcu_enable_obss_spr(struct mt7915_phy *phy, u8 action, u8 val) { struct mt7915_dev *dev = phy->dev; struct mt7915_mcu_sr_ctrl req = { .action = action, .argnum = 1, .band_idx = phy->mt76->band_idx, .val = cpu_to_le32(val), }; return mt76_mcu_send_msg(&dev->mt76, MCU_EXT_CMD(SET_SPR), &req, sizeof(req), true); } static int mt7915_mcu_set_obss_spr_pd(struct mt7915_phy *phy, struct ieee80211_he_obss_pd *he_obss_pd) { struct mt7915_dev *dev = phy->dev; struct { struct mt7915_mcu_sr_ctrl ctrl; struct { u8 pd_th_non_srg; u8 pd_th_srg; u8 period_offs; u8 rcpi_src; __le16 obss_pd_min; __le16 obss_pd_min_srg; u8 resp_txpwr_mode; u8 txpwr_restrict_mode; u8 txpwr_ref; u8 rsv[3]; } __packed param; } __packed req = { .ctrl = { .action = SPR_SET_PARAM, .argnum = 9, .band_idx = phy->mt76->band_idx, }, }; int ret; u8 max_th = 82, non_srg_max_th = 62; /* disable firmware dynamical PD asjustment */ ret = mt7915_mcu_enable_obss_spr(phy, SPR_ENABLE_DPD, false); if (ret) return ret; if (he_obss_pd->sr_ctrl & IEEE80211_HE_SPR_NON_SRG_OBSS_PD_SR_DISALLOWED) req.param.pd_th_non_srg = max_th; else if (he_obss_pd->sr_ctrl & IEEE80211_HE_SPR_NON_SRG_OFFSET_PRESENT) req.param.pd_th_non_srg = max_th - he_obss_pd->non_srg_max_offset; else req.param.pd_th_non_srg = non_srg_max_th; if (he_obss_pd->sr_ctrl & IEEE80211_HE_SPR_SRG_INFORMATION_PRESENT) req.param.pd_th_srg = max_th - he_obss_pd->max_offset; req.param.obss_pd_min = cpu_to_le16(82); req.param.obss_pd_min_srg = cpu_to_le16(82); req.param.txpwr_restrict_mode = 2; req.param.txpwr_ref = 21; return mt76_mcu_send_msg(&dev->mt76, MCU_EXT_CMD(SET_SPR), &req, sizeof(req), true); } static int mt7915_mcu_set_obss_spr_siga(struct mt7915_phy *phy, struct ieee80211_vif *vif, struct ieee80211_he_obss_pd *he_obss_pd) { struct mt7915_vif *mvif = (struct mt7915_vif *)vif->drv_priv; struct mt7915_dev *dev = phy->dev; u8 omac = mvif->mt76.omac_idx; struct { struct mt7915_mcu_sr_ctrl ctrl; struct { u8 omac; u8 rsv[3]; u8 flag[20]; } __packed siga; } __packed req = { .ctrl = { .action = SPR_SET_SIGA, .argnum = 1, .band_idx = phy->mt76->band_idx, }, .siga = { .omac = omac > HW_BSSID_MAX ? omac - 12 : omac, }, }; int ret; if (he_obss_pd->sr_ctrl & IEEE80211_HE_SPR_HESIGA_SR_VAL15_ALLOWED) req.siga.flag[req.siga.omac] = 0xf; else return 0; /* switch to normal AP mode */ ret = mt7915_mcu_enable_obss_spr(phy, SPR_ENABLE_MODE, 0); if (ret) return ret; return mt76_mcu_send_msg(&dev->mt76, MCU_EXT_CMD(SET_SPR), &req, sizeof(req), true); } static int mt7915_mcu_set_obss_spr_bitmap(struct mt7915_phy *phy, struct ieee80211_he_obss_pd *he_obss_pd) { struct mt7915_dev *dev = phy->dev; struct { struct mt7915_mcu_sr_ctrl ctrl; struct { __le32 color_l[2]; __le32 color_h[2]; __le32 bssid_l[2]; __le32 bssid_h[2]; } __packed bitmap; } __packed req = { .ctrl = { .action = SPR_SET_SRG_BITMAP, .argnum = 4, .band_idx = phy->mt76->band_idx, }, }; u32 bitmap; memcpy(&bitmap, he_obss_pd->bss_color_bitmap, sizeof(bitmap)); req.bitmap.color_l[req.ctrl.band_idx] = cpu_to_le32(bitmap); memcpy(&bitmap, he_obss_pd->bss_color_bitmap + 4, sizeof(bitmap)); req.bitmap.color_h[req.ctrl.band_idx] = cpu_to_le32(bitmap); memcpy(&bitmap, he_obss_pd->partial_bssid_bitmap, sizeof(bitmap)); req.bitmap.bssid_l[req.ctrl.band_idx] = cpu_to_le32(bitmap); memcpy(&bitmap, he_obss_pd->partial_bssid_bitmap + 4, sizeof(bitmap)); req.bitmap.bssid_h[req.ctrl.band_idx] = cpu_to_le32(bitmap); return mt76_mcu_send_msg(&dev->mt76, MCU_EXT_CMD(SET_SPR), &req, sizeof(req), true); } int mt7915_mcu_add_obss_spr(struct mt7915_phy *phy, struct ieee80211_vif *vif, struct ieee80211_he_obss_pd *he_obss_pd) { int ret; /* enable firmware scene detection algorithms */ ret = mt7915_mcu_enable_obss_spr(phy, SPR_ENABLE_SD, sr_scene_detect); if (ret) return ret; /* firmware dynamically adjusts PD threshold so skip manual control */ if (sr_scene_detect && !he_obss_pd->enable) return 0; /* enable spatial reuse */ ret = mt7915_mcu_enable_obss_spr(phy, SPR_ENABLE, he_obss_pd->enable); if (ret) return ret; if (sr_scene_detect || !he_obss_pd->enable) return 0; ret = mt7915_mcu_enable_obss_spr(phy, SPR_ENABLE_TX, true); if (ret) return ret; /* set SRG/non-SRG OBSS PD threshold */ ret = mt7915_mcu_set_obss_spr_pd(phy, he_obss_pd); if (ret) return ret; /* Set SR prohibit */ ret = mt7915_mcu_set_obss_spr_siga(phy, vif, he_obss_pd); if (ret) return ret; /* set SRG BSS color/BSSID bitmap */ return mt7915_mcu_set_obss_spr_bitmap(phy, he_obss_pd); } int mt7915_mcu_get_rx_rate(struct mt7915_phy *phy, struct ieee80211_vif *vif, struct ieee80211_sta *sta, struct rate_info *rate) { struct mt7915_vif *mvif = (struct mt7915_vif *)vif->drv_priv; struct mt7915_sta *msta = (struct mt7915_sta *)sta->drv_priv; struct mt7915_dev *dev = phy->dev; struct mt76_phy *mphy = phy->mt76; struct { u8 category; u8 band; __le16 wcid; } __packed req = { .category = MCU_PHY_STATE_CONTENTION_RX_RATE, .band = mvif->mt76.band_idx, .wcid = cpu_to_le16(msta->wcid.idx), }; struct ieee80211_supported_band *sband; struct mt7915_mcu_phy_rx_info *res; struct sk_buff *skb; int ret; bool cck = false; ret = mt76_mcu_send_and_get_msg(&dev->mt76, MCU_EXT_CMD(PHY_STAT_INFO), &req, sizeof(req), true, &skb); if (ret) return ret; res = (struct mt7915_mcu_phy_rx_info *)skb->data; rate->mcs = res->rate; rate->nss = res->nsts + 1; switch (res->mode) { case MT_PHY_TYPE_CCK: cck = true; fallthrough; case MT_PHY_TYPE_OFDM: if (mphy->chandef.chan->band == NL80211_BAND_5GHZ) sband = &mphy->sband_5g.sband; else if (mphy->chandef.chan->band == NL80211_BAND_6GHZ) sband = &mphy->sband_6g.sband; else sband = &mphy->sband_2g.sband; rate->mcs = mt76_get_rate(&dev->mt76, sband, rate->mcs, cck); rate->legacy = sband->bitrates[rate->mcs].bitrate; break; case MT_PHY_TYPE_HT: case MT_PHY_TYPE_HT_GF: if (rate->mcs > 31) { ret = -EINVAL; goto out; } rate->flags = RATE_INFO_FLAGS_MCS; if (res->gi) rate->flags |= RATE_INFO_FLAGS_SHORT_GI; break; case MT_PHY_TYPE_VHT: if (rate->mcs > 9) { ret = -EINVAL; goto out; } rate->flags = RATE_INFO_FLAGS_VHT_MCS; if (res->gi) rate->flags |= RATE_INFO_FLAGS_SHORT_GI; break; case MT_PHY_TYPE_HE_SU: case MT_PHY_TYPE_HE_EXT_SU: case MT_PHY_TYPE_HE_TB: case MT_PHY_TYPE_HE_MU: if (res->gi > NL80211_RATE_INFO_HE_GI_3_2 || rate->mcs > 11) { ret = -EINVAL; goto out; } rate->he_gi = res->gi; rate->flags = RATE_INFO_FLAGS_HE_MCS; break; default: ret = -EINVAL; goto out; } switch (res->bw) { case IEEE80211_STA_RX_BW_160: rate->bw = RATE_INFO_BW_160; break; case IEEE80211_STA_RX_BW_80: rate->bw = RATE_INFO_BW_80; break; case IEEE80211_STA_RX_BW_40: rate->bw = RATE_INFO_BW_40; break; default: rate->bw = RATE_INFO_BW_20; break; } out: dev_kfree_skb(skb); return ret; } int mt7915_mcu_update_bss_color(struct mt7915_dev *dev, struct ieee80211_vif *vif, struct cfg80211_he_bss_color *he_bss_color) { int len = sizeof(struct sta_req_hdr) + sizeof(struct bss_info_color); struct mt7915_vif *mvif = (struct mt7915_vif *)vif->drv_priv; struct bss_info_color *bss_color; struct sk_buff *skb; struct tlv *tlv; skb = __mt76_connac_mcu_alloc_sta_req(&dev->mt76, &mvif->mt76, NULL, len); if (IS_ERR(skb)) return PTR_ERR(skb); tlv = mt76_connac_mcu_add_tlv(skb, BSS_INFO_BSS_COLOR, sizeof(*bss_color)); bss_color = (struct bss_info_color *)tlv; bss_color->disable = !he_bss_color->enabled; bss_color->color = he_bss_color->color; return mt76_mcu_skb_send_msg(&dev->mt76, skb, MCU_EXT_CMD(BSS_INFO_UPDATE), true); } #define TWT_AGRT_TRIGGER BIT(0) #define TWT_AGRT_ANNOUNCE BIT(1) #define TWT_AGRT_PROTECT BIT(2) int mt7915_mcu_twt_agrt_update(struct mt7915_dev *dev, struct mt7915_vif *mvif, struct mt7915_twt_flow *flow, int cmd) { struct { u8 tbl_idx; u8 cmd; u8 own_mac_idx; u8 flowid; /* 0xff for group id */ __le16 peer_id; /* specify the peer_id (msb=0) * or group_id (msb=1) */ u8 duration; /* 256 us */ u8 bss_idx; __le64 start_tsf; __le16 mantissa; u8 exponent; u8 is_ap; u8 agrt_params; u8 rsv[23]; } __packed req = { .tbl_idx = flow->table_id, .cmd = cmd, .own_mac_idx = mvif->mt76.omac_idx, .flowid = flow->id, .peer_id = cpu_to_le16(flow->wcid), .duration = flow->duration, .bss_idx = mvif->mt76.idx, .start_tsf = cpu_to_le64(flow->tsf), .mantissa = flow->mantissa, .exponent = flow->exp, .is_ap = true, }; if (flow->protection) req.agrt_params |= TWT_AGRT_PROTECT; if (!flow->flowtype) req.agrt_params |= TWT_AGRT_ANNOUNCE; if (flow->trigger) req.agrt_params |= TWT_AGRT_TRIGGER; return mt76_mcu_send_msg(&dev->mt76, MCU_EXT_CMD(TWT_AGRT_UPDATE), &req, sizeof(req), true); } int mt7915_mcu_wed_wa_tx_stats(struct mt7915_dev *dev, u16 wlan_idx) { struct { __le32 cmd; __le32 arg0; __le32 arg1; __le16 arg2; } __packed req = { .cmd = cpu_to_le32(0x15), }; struct mt7915_mcu_wa_tx_stat { __le16 wcid; u8 __rsv2[2]; /* tx_bytes is deprecated since WA byte counter uses u32, * which easily leads to overflow. */ __le32 tx_bytes; __le32 tx_packets; } __packed *res; struct mt76_wcid *wcid; struct sk_buff *skb; int ret, len; u16 ret_wcid; if (is_mt7915(&dev->mt76)) { req.arg0 = cpu_to_le32(wlan_idx); len = sizeof(req) - sizeof(req.arg2); } else { req.arg0 = cpu_to_le32(1); req.arg2 = cpu_to_le16(wlan_idx); len = sizeof(req); } ret = mt76_mcu_send_and_get_msg(&dev->mt76, MCU_WA_PARAM_CMD(QUERY), &req, len, true, &skb); if (ret) return ret; if (!is_mt7915(&dev->mt76)) skb_pull(skb, 4); res = (struct mt7915_mcu_wa_tx_stat *)skb->data; ret_wcid = le16_to_cpu(res->wcid); if (is_mt7915(&dev->mt76)) ret_wcid &= 0xff; if (ret_wcid != wlan_idx) { ret = -EINVAL; goto out; } rcu_read_lock(); wcid = mt76_wcid_ptr(dev, wlan_idx); if (wcid) wcid->stats.tx_packets += le32_to_cpu(res->tx_packets); else ret = -EINVAL; rcu_read_unlock(); out: dev_kfree_skb(skb); return ret; } int mt7915_mcu_rf_regval(struct mt7915_dev *dev, u32 regidx, u32 *val, bool set) { struct { __le32 idx; __le32 ofs; __le32 data; } __packed req = { .idx = cpu_to_le32(u32_get_bits(regidx, GENMASK(31, 24))), .ofs = cpu_to_le32(u32_get_bits(regidx, GENMASK(23, 0))), .data = set ? cpu_to_le32(*val) : 0, }; struct sk_buff *skb; int ret; if (set) return mt76_mcu_send_msg(&dev->mt76, MCU_EXT_CMD(RF_REG_ACCESS), &req, sizeof(req), false); ret = mt76_mcu_send_and_get_msg(&dev->mt76, MCU_EXT_QUERY(RF_REG_ACCESS), &req, sizeof(req), true, &skb); if (ret) return ret; *val = le32_to_cpu(*(__le32 *)(skb->data + 8)); dev_kfree_skb(skb); return 0; } diff --git a/sys/contrib/dev/mediatek/mt76/mt7915/mmio.c b/sys/contrib/dev/mediatek/mt76/mt7915/mmio.c index 83a828b7c578..ace2441d41a9 100644 --- a/sys/contrib/dev/mediatek/mt76/mt7915/mmio.c +++ b/sys/contrib/dev/mediatek/mt76/mt7915/mmio.c @@ -1,1012 +1,1016 @@ // SPDX-License-Identifier: ISC /* Copyright (C) 2020 MediaTek Inc. */ +#if defined(__FreeBSD__) +#define LINUXKPI_PARAM_PREFIX mt7915_ +#endif + #include #include #include #include #include #include "mt7915.h" #include "mac.h" #include "mcu.h" #include "../trace.h" #include "../dma.h" static bool wed_enable; module_param(wed_enable, bool, 0644); MODULE_PARM_DESC(wed_enable, "Enable Wireless Ethernet Dispatch support"); static const u32 mt7915_reg[] = { [INT_SOURCE_CSR] = 0xd7010, [INT_MASK_CSR] = 0xd7014, [INT1_SOURCE_CSR] = 0xd7088, [INT1_MASK_CSR] = 0xd708c, [INT_MCU_CMD_SOURCE] = 0xd51f0, [INT_MCU_CMD_EVENT] = 0x3108, [WFDMA0_ADDR] = 0xd4000, [WFDMA0_PCIE1_ADDR] = 0xd8000, [WFDMA_EXT_CSR_ADDR] = 0xd7000, [CBTOP1_PHY_END] = 0x77ffffff, [INFRA_MCU_ADDR_END] = 0x7c3fffff, [FW_ASSERT_STAT_ADDR] = 0x219848, [FW_EXCEPT_TYPE_ADDR] = 0x21987c, [FW_EXCEPT_COUNT_ADDR] = 0x219848, [FW_CIRQ_COUNT_ADDR] = 0x216f94, [FW_CIRQ_IDX_ADDR] = 0x216ef8, [FW_CIRQ_LISR_ADDR] = 0x2170ac, [FW_TASK_ID_ADDR] = 0x216f90, [FW_TASK_IDX_ADDR] = 0x216f9c, [FW_TASK_QID1_ADDR] = 0x219680, [FW_TASK_QID2_ADDR] = 0x219760, [FW_TASK_START_ADDR] = 0x219558, [FW_TASK_END_ADDR] = 0x219554, [FW_TASK_SIZE_ADDR] = 0x219560, [FW_LAST_MSG_ID_ADDR] = 0x216f70, [FW_EINT_INFO_ADDR] = 0x219818, [FW_SCHED_INFO_ADDR] = 0x219828, [SWDEF_BASE_ADDR] = 0x41f200, [TXQ_WED_RING_BASE] = 0xd7300, [RXQ_WED_RING_BASE] = 0xd7410, [RXQ_WED_DATA_RING_BASE] = 0xd4500, }; static const u32 mt7916_reg[] = { [INT_SOURCE_CSR] = 0xd4200, [INT_MASK_CSR] = 0xd4204, [INT1_SOURCE_CSR] = 0xd8200, [INT1_MASK_CSR] = 0xd8204, [INT_MCU_CMD_SOURCE] = 0xd41f0, [INT_MCU_CMD_EVENT] = 0x2108, [WFDMA0_ADDR] = 0xd4000, [WFDMA0_PCIE1_ADDR] = 0xd8000, [WFDMA_EXT_CSR_ADDR] = 0xd7000, [CBTOP1_PHY_END] = 0x7fffffff, [INFRA_MCU_ADDR_END] = 0x7c085fff, [FW_ASSERT_STAT_ADDR] = 0x02204c14, [FW_EXCEPT_TYPE_ADDR] = 0x022051a4, [FW_EXCEPT_COUNT_ADDR] = 0x022050bc, [FW_CIRQ_COUNT_ADDR] = 0x022001ac, [FW_CIRQ_IDX_ADDR] = 0x02204f84, [FW_CIRQ_LISR_ADDR] = 0x022050d0, [FW_TASK_ID_ADDR] = 0x0220406c, [FW_TASK_IDX_ADDR] = 0x0220500c, [FW_TASK_QID1_ADDR] = 0x022028c8, [FW_TASK_QID2_ADDR] = 0x02202a38, [FW_TASK_START_ADDR] = 0x0220286c, [FW_TASK_END_ADDR] = 0x02202870, [FW_TASK_SIZE_ADDR] = 0x02202878, [FW_LAST_MSG_ID_ADDR] = 0x02204fe8, [FW_EINT_INFO_ADDR] = 0x0220525c, [FW_SCHED_INFO_ADDR] = 0x0220516c, [SWDEF_BASE_ADDR] = 0x411400, [TXQ_WED_RING_BASE] = 0xd7300, [RXQ_WED_RING_BASE] = 0xd7410, [RXQ_WED_DATA_RING_BASE] = 0xd4540, }; static const u32 mt7986_reg[] = { [INT_SOURCE_CSR] = 0x24200, [INT_MASK_CSR] = 0x24204, [INT1_SOURCE_CSR] = 0x28200, [INT1_MASK_CSR] = 0x28204, [INT_MCU_CMD_SOURCE] = 0x241f0, [INT_MCU_CMD_EVENT] = 0x54000108, [WFDMA0_ADDR] = 0x24000, [WFDMA0_PCIE1_ADDR] = 0x28000, [WFDMA_EXT_CSR_ADDR] = 0x27000, [CBTOP1_PHY_END] = 0x7fffffff, [INFRA_MCU_ADDR_END] = 0x7c085fff, [FW_ASSERT_STAT_ADDR] = 0x02204b54, [FW_EXCEPT_TYPE_ADDR] = 0x022050dc, [FW_EXCEPT_COUNT_ADDR] = 0x02204ffc, [FW_CIRQ_COUNT_ADDR] = 0x022001ac, [FW_CIRQ_IDX_ADDR] = 0x02204ec4, [FW_CIRQ_LISR_ADDR] = 0x02205010, [FW_TASK_ID_ADDR] = 0x02204fac, [FW_TASK_IDX_ADDR] = 0x02204f4c, [FW_TASK_QID1_ADDR] = 0x02202814, [FW_TASK_QID2_ADDR] = 0x02202984, [FW_TASK_START_ADDR] = 0x022027b8, [FW_TASK_END_ADDR] = 0x022027bc, [FW_TASK_SIZE_ADDR] = 0x022027c4, [FW_LAST_MSG_ID_ADDR] = 0x02204f28, [FW_EINT_INFO_ADDR] = 0x02205194, [FW_SCHED_INFO_ADDR] = 0x022051a4, [SWDEF_BASE_ADDR] = 0x411400, [TXQ_WED_RING_BASE] = 0x24420, [RXQ_WED_RING_BASE] = 0x24520, [RXQ_WED_DATA_RING_BASE] = 0x24540, }; static const u32 mt7915_offs[] = { [TMAC_CDTR] = 0x090, [TMAC_ODTR] = 0x094, [TMAC_ATCR] = 0x098, [TMAC_TRCR0] = 0x09c, [TMAC_ICR0] = 0x0a4, [TMAC_ICR1] = 0x0b4, [TMAC_CTCR0] = 0x0f4, [TMAC_TFCR0] = 0x1e0, [MDP_BNRCFR0] = 0x070, [MDP_BNRCFR1] = 0x074, [ARB_DRNGR0] = 0x194, [ARB_SCR] = 0x080, [RMAC_MIB_AIRTIME14] = 0x3b8, [AGG_AWSCR0] = 0x05c, [AGG_PCR0] = 0x06c, [AGG_ACR0] = 0x084, [AGG_ACR4] = 0x08c, [AGG_MRCR] = 0x098, [AGG_ATCR0] = 0x0ec, [AGG_ATCR1] = 0x0f0, [AGG_ATCR3] = 0x0f4, [LPON_UTTR0] = 0x080, [LPON_UTTR1] = 0x084, [LPON_FRCR] = 0x314, [MIB_SDR3] = 0x014, [MIB_SDR4] = 0x018, [MIB_SDR5] = 0x01c, [MIB_SDR7] = 0x024, [MIB_SDR8] = 0x028, [MIB_SDR9] = 0x02c, [MIB_SDR10] = 0x030, [MIB_SDR11] = 0x034, [MIB_SDR12] = 0x038, [MIB_SDR13] = 0x03c, [MIB_SDR14] = 0x040, [MIB_SDR15] = 0x044, [MIB_SDR16] = 0x048, [MIB_SDR17] = 0x04c, [MIB_SDR18] = 0x050, [MIB_SDR19] = 0x054, [MIB_SDR20] = 0x058, [MIB_SDR21] = 0x05c, [MIB_SDR22] = 0x060, [MIB_SDR23] = 0x064, [MIB_SDR24] = 0x068, [MIB_SDR25] = 0x06c, [MIB_SDR27] = 0x074, [MIB_SDR28] = 0x078, [MIB_SDR29] = 0x07c, [MIB_SDRVEC] = 0x080, [MIB_SDR31] = 0x084, [MIB_SDR32] = 0x088, [MIB_SDRMUBF] = 0x090, [MIB_DR8] = 0x0c0, [MIB_DR9] = 0x0c4, [MIB_DR11] = 0x0cc, [MIB_MB_SDR0] = 0x100, [MIB_MB_SDR1] = 0x104, [TX_AGG_CNT] = 0x0a8, [TX_AGG_CNT2] = 0x164, [MIB_ARNG] = 0x4b8, [WTBLON_TOP_WDUCR] = 0x0, [WTBL_UPDATE] = 0x030, [PLE_FL_Q_EMPTY] = 0x0b0, [PLE_FL_Q_CTRL] = 0x1b0, [PLE_AC_QEMPTY] = 0x500, [PLE_FREEPG_CNT] = 0x100, [PLE_FREEPG_HEAD_TAIL] = 0x104, [PLE_PG_HIF_GROUP] = 0x110, [PLE_HIF_PG_INFO] = 0x114, [AC_OFFSET] = 0x040, [ETBF_PAR_RPT0] = 0x068, }; static const u32 mt7916_offs[] = { [TMAC_CDTR] = 0x0c8, [TMAC_ODTR] = 0x0cc, [TMAC_ATCR] = 0x00c, [TMAC_TRCR0] = 0x010, [TMAC_ICR0] = 0x014, [TMAC_ICR1] = 0x018, [TMAC_CTCR0] = 0x114, [TMAC_TFCR0] = 0x0e4, [MDP_BNRCFR0] = 0x090, [MDP_BNRCFR1] = 0x094, [ARB_DRNGR0] = 0x1e0, [ARB_SCR] = 0x000, [RMAC_MIB_AIRTIME14] = 0x0398, [AGG_AWSCR0] = 0x030, [AGG_PCR0] = 0x040, [AGG_ACR0] = 0x054, [AGG_ACR4] = 0x05c, [AGG_MRCR] = 0x068, [AGG_ATCR0] = 0x1a4, [AGG_ATCR1] = 0x1a8, [AGG_ATCR3] = 0x080, [LPON_UTTR0] = 0x360, [LPON_UTTR1] = 0x364, [LPON_FRCR] = 0x37c, [MIB_SDR3] = 0x698, [MIB_SDR4] = 0x788, [MIB_SDR5] = 0x780, [MIB_SDR7] = 0x5a8, [MIB_SDR8] = 0x78c, [MIB_SDR9] = 0x024, [MIB_SDR10] = 0x76c, [MIB_SDR11] = 0x790, [MIB_SDR12] = 0x558, [MIB_SDR13] = 0x560, [MIB_SDR14] = 0x564, [MIB_SDR15] = 0x568, [MIB_SDR16] = 0x7fc, [MIB_SDR17] = 0x800, [MIB_SDR18] = 0x030, [MIB_SDR19] = 0x5ac, [MIB_SDR20] = 0x5b0, [MIB_SDR21] = 0x5b4, [MIB_SDR22] = 0x770, [MIB_SDR23] = 0x774, [MIB_SDR24] = 0x778, [MIB_SDR25] = 0x77c, [MIB_SDR27] = 0x080, [MIB_SDR28] = 0x084, [MIB_SDR29] = 0x650, [MIB_SDRVEC] = 0x5a8, [MIB_SDR31] = 0x55c, [MIB_SDR32] = 0x7a8, [MIB_SDRMUBF] = 0x7ac, [MIB_DR8] = 0x56c, [MIB_DR9] = 0x570, [MIB_DR11] = 0x574, [MIB_MB_SDR0] = 0x688, [MIB_MB_SDR1] = 0x690, [TX_AGG_CNT] = 0x7dc, [TX_AGG_CNT2] = 0x7ec, [MIB_ARNG] = 0x0b0, [WTBLON_TOP_WDUCR] = 0x200, [WTBL_UPDATE] = 0x230, [PLE_FL_Q_EMPTY] = 0x360, [PLE_FL_Q_CTRL] = 0x3e0, [PLE_AC_QEMPTY] = 0x600, [PLE_FREEPG_CNT] = 0x380, [PLE_FREEPG_HEAD_TAIL] = 0x384, [PLE_PG_HIF_GROUP] = 0x00c, [PLE_HIF_PG_INFO] = 0x388, [AC_OFFSET] = 0x080, [ETBF_PAR_RPT0] = 0x100, }; static const struct mt76_connac_reg_map mt7915_reg_map[] = { { 0x00400000, 0x80000, 0x10000 }, /* WF_MCU_SYSRAM */ { 0x00410000, 0x90000, 0x10000 }, /* WF_MCU_SYSRAM (configure regs) */ { 0x40000000, 0x70000, 0x10000 }, /* WF_UMAC_SYSRAM */ { 0x54000000, 0x02000, 0x01000 }, /* WFDMA PCIE0 MCU DMA0 */ { 0x55000000, 0x03000, 0x01000 }, /* WFDMA PCIE0 MCU DMA1 */ { 0x58000000, 0x06000, 0x01000 }, /* WFDMA PCIE1 MCU DMA0 (MEM_DMA) */ { 0x59000000, 0x07000, 0x01000 }, /* WFDMA PCIE1 MCU DMA1 */ { 0x7c000000, 0xf0000, 0x10000 }, /* CONN_INFRA */ { 0x7c020000, 0xd0000, 0x10000 }, /* CONN_INFRA, WFDMA */ { 0x80020000, 0xb0000, 0x10000 }, /* WF_TOP_MISC_OFF */ { 0x81020000, 0xc0000, 0x10000 }, /* WF_TOP_MISC_ON */ { 0x820c0000, 0x08000, 0x04000 }, /* WF_UMAC_TOP (PLE) */ { 0x820c8000, 0x0c000, 0x02000 }, /* WF_UMAC_TOP (PSE) */ { 0x820cc000, 0x0e000, 0x02000 }, /* WF_UMAC_TOP (PP) */ { 0x820ce000, 0x21c00, 0x00200 }, /* WF_LMAC_TOP (WF_SEC) */ { 0x820cf000, 0x22000, 0x01000 }, /* WF_LMAC_TOP (WF_PF) */ { 0x820d0000, 0x30000, 0x10000 }, /* WF_LMAC_TOP (WF_WTBLON) */ { 0x820e0000, 0x20000, 0x00400 }, /* WF_LMAC_TOP BN0 (WF_CFG) */ { 0x820e1000, 0x20400, 0x00200 }, /* WF_LMAC_TOP BN0 (WF_TRB) */ { 0x820e2000, 0x20800, 0x00400 }, /* WF_LMAC_TOP BN0 (WF_AGG) */ { 0x820e3000, 0x20c00, 0x00400 }, /* WF_LMAC_TOP BN0 (WF_ARB) */ { 0x820e4000, 0x21000, 0x00400 }, /* WF_LMAC_TOP BN0 (WF_TMAC) */ { 0x820e5000, 0x21400, 0x00800 }, /* WF_LMAC_TOP BN0 (WF_RMAC) */ { 0x820e7000, 0x21e00, 0x00200 }, /* WF_LMAC_TOP BN0 (WF_DMA) */ { 0x820e9000, 0x23400, 0x00200 }, /* WF_LMAC_TOP BN0 (WF_WTBLOFF) */ { 0x820ea000, 0x24000, 0x00200 }, /* WF_LMAC_TOP BN0 (WF_ETBF) */ { 0x820eb000, 0x24200, 0x00400 }, /* WF_LMAC_TOP BN0 (WF_LPON) */ { 0x820ec000, 0x24600, 0x00200 }, /* WF_LMAC_TOP BN0 (WF_INT) */ { 0x820ed000, 0x24800, 0x00800 }, /* WF_LMAC_TOP BN0 (WF_MIB) */ { 0x820f0000, 0xa0000, 0x00400 }, /* WF_LMAC_TOP BN1 (WF_CFG) */ { 0x820f1000, 0xa0600, 0x00200 }, /* WF_LMAC_TOP BN1 (WF_TRB) */ { 0x820f2000, 0xa0800, 0x00400 }, /* WF_LMAC_TOP BN1 (WF_AGG) */ { 0x820f3000, 0xa0c00, 0x00400 }, /* WF_LMAC_TOP BN1 (WF_ARB) */ { 0x820f4000, 0xa1000, 0x00400 }, /* WF_LMAC_TOP BN1 (WF_TMAC) */ { 0x820f5000, 0xa1400, 0x00800 }, /* WF_LMAC_TOP BN1 (WF_RMAC) */ { 0x820f7000, 0xa1e00, 0x00200 }, /* WF_LMAC_TOP BN1 (WF_DMA) */ { 0x820f9000, 0xa3400, 0x00200 }, /* WF_LMAC_TOP BN1 (WF_WTBLOFF) */ { 0x820fa000, 0xa4000, 0x00200 }, /* WF_LMAC_TOP BN1 (WF_ETBF) */ { 0x820fb000, 0xa4200, 0x00400 }, /* WF_LMAC_TOP BN1 (WF_LPON) */ { 0x820fc000, 0xa4600, 0x00200 }, /* WF_LMAC_TOP BN1 (WF_INT) */ { 0x820fd000, 0xa4800, 0x00800 }, /* WF_LMAC_TOP BN1 (WF_MIB) */ { 0x0, 0x0, 0x0 }, /* imply end of search */ }; static const struct mt76_connac_reg_map mt7916_reg_map[] = { { 0x54000000, 0x02000, 0x01000 }, /* WFDMA_0 (PCIE0 MCU DMA0) */ { 0x55000000, 0x03000, 0x01000 }, /* WFDMA_1 (PCIE0 MCU DMA1) */ { 0x56000000, 0x04000, 0x01000 }, /* WFDMA_2 (Reserved) */ { 0x57000000, 0x05000, 0x01000 }, /* WFDMA_3 (MCU wrap CR) */ { 0x58000000, 0x06000, 0x01000 }, /* WFDMA_4 (PCIE1 MCU DMA0) */ { 0x59000000, 0x07000, 0x01000 }, /* WFDMA_5 (PCIE1 MCU DMA1) */ { 0x820c0000, 0x08000, 0x04000 }, /* WF_UMAC_TOP (PLE) */ { 0x820c8000, 0x0c000, 0x02000 }, /* WF_UMAC_TOP (PSE) */ { 0x820cc000, 0x0e000, 0x02000 }, /* WF_UMAC_TOP (PP) */ { 0x820e0000, 0x20000, 0x00400 }, /* WF_LMAC_TOP BN0 (WF_CFG) */ { 0x820e1000, 0x20400, 0x00200 }, /* WF_LMAC_TOP BN0 (WF_TRB) */ { 0x820e2000, 0x20800, 0x00400 }, /* WF_LMAC_TOP BN0 (WF_AGG) */ { 0x820e3000, 0x20c00, 0x00400 }, /* WF_LMAC_TOP BN0 (WF_ARB) */ { 0x820e4000, 0x21000, 0x00400 }, /* WF_LMAC_TOP BN0 (WF_TMAC) */ { 0x820e5000, 0x21400, 0x00800 }, /* WF_LMAC_TOP BN0 (WF_RMAC) */ { 0x820ce000, 0x21c00, 0x00200 }, /* WF_LMAC_TOP (WF_SEC) */ { 0x820e7000, 0x21e00, 0x00200 }, /* WF_LMAC_TOP BN0 (WF_DMA) */ { 0x820cf000, 0x22000, 0x01000 }, /* WF_LMAC_TOP (WF_PF) */ { 0x820e9000, 0x23400, 0x00200 }, /* WF_LMAC_TOP BN0 (WF_WTBLOFF) */ { 0x820ea000, 0x24000, 0x00200 }, /* WF_LMAC_TOP BN0 (WF_ETBF) */ { 0x820eb000, 0x24200, 0x00400 }, /* WF_LMAC_TOP BN0 (WF_LPON) */ { 0x820ec000, 0x24600, 0x00200 }, /* WF_LMAC_TOP BN0 (WF_INT) */ { 0x820ed000, 0x24800, 0x00800 }, /* WF_LMAC_TOP BN0 (WF_MIB) */ { 0x820ca000, 0x26000, 0x02000 }, /* WF_LMAC_TOP BN0 (WF_MUCOP) */ { 0x820d0000, 0x30000, 0x10000 }, /* WF_LMAC_TOP (WF_WTBLON) */ { 0x00400000, 0x80000, 0x10000 }, /* WF_MCU_SYSRAM */ { 0x00410000, 0x90000, 0x10000 }, /* WF_MCU_SYSRAM (configure cr) */ { 0x820f0000, 0xa0000, 0x00400 }, /* WF_LMAC_TOP BN1 (WF_CFG) */ { 0x820f1000, 0xa0600, 0x00200 }, /* WF_LMAC_TOP BN1 (WF_TRB) */ { 0x820f2000, 0xa0800, 0x00400 }, /* WF_LMAC_TOP BN1 (WF_AGG) */ { 0x820f3000, 0xa0c00, 0x00400 }, /* WF_LMAC_TOP BN1 (WF_ARB) */ { 0x820f4000, 0xa1000, 0x00400 }, /* WF_LMAC_TOP BN1 (WF_TMAC) */ { 0x820f5000, 0xa1400, 0x00800 }, /* WF_LMAC_TOP BN1 (WF_RMAC) */ { 0x820f7000, 0xa1e00, 0x00200 }, /* WF_LMAC_TOP BN1 (WF_DMA) */ { 0x820f9000, 0xa3400, 0x00200 }, /* WF_LMAC_TOP BN1 (WF_WTBLOFF) */ { 0x820fa000, 0xa4000, 0x00200 }, /* WF_LMAC_TOP BN1 (WF_ETBF) */ { 0x820fb000, 0xa4200, 0x00400 }, /* WF_LMAC_TOP BN1 (WF_LPON) */ { 0x820fc000, 0xa4600, 0x00200 }, /* WF_LMAC_TOP BN1 (WF_INT) */ { 0x820fd000, 0xa4800, 0x00800 }, /* WF_LMAC_TOP BN1 (WF_MIB) */ { 0x820c4000, 0xa8000, 0x01000 }, /* WF_LMAC_TOP (WF_UWTBL ) */ { 0x820b0000, 0xae000, 0x01000 }, /* [APB2] WFSYS_ON */ { 0x80020000, 0xb0000, 0x10000 }, /* WF_TOP_MISC_OFF */ { 0x81020000, 0xc0000, 0x10000 }, /* WF_TOP_MISC_ON */ { 0x0, 0x0, 0x0 }, /* imply end of search */ }; static const struct mt76_connac_reg_map mt7986_reg_map[] = { { 0x54000000, 0x402000, 0x01000 }, /* WFDMA_0 (PCIE0 MCU DMA0) */ { 0x55000000, 0x403000, 0x01000 }, /* WFDMA_1 (PCIE0 MCU DMA1) */ { 0x56000000, 0x404000, 0x01000 }, /* WFDMA_2 (Reserved) */ { 0x57000000, 0x405000, 0x01000 }, /* WFDMA_3 (MCU wrap CR) */ { 0x58000000, 0x406000, 0x01000 }, /* WFDMA_4 (PCIE1 MCU DMA0) */ { 0x59000000, 0x407000, 0x01000 }, /* WFDMA_5 (PCIE1 MCU DMA1) */ { 0x820c0000, 0x408000, 0x04000 }, /* WF_UMAC_TOP (PLE) */ { 0x820c8000, 0x40c000, 0x02000 }, /* WF_UMAC_TOP (PSE) */ { 0x820cc000, 0x40e000, 0x02000 }, /* WF_UMAC_TOP (PP) */ { 0x820e0000, 0x420000, 0x00400 }, /* WF_LMAC_TOP BN0 (WF_CFG) */ { 0x820e1000, 0x420400, 0x00200 }, /* WF_LMAC_TOP BN0 (WF_TRB) */ { 0x820e2000, 0x420800, 0x00400 }, /* WF_LMAC_TOP BN0 (WF_AGG) */ { 0x820e3000, 0x420c00, 0x00400 }, /* WF_LMAC_TOP BN0 (WF_ARB) */ { 0x820e4000, 0x421000, 0x00400 }, /* WF_LMAC_TOP BN0 (WF_TMAC) */ { 0x820e5000, 0x421400, 0x00800 }, /* WF_LMAC_TOP BN0 (WF_RMAC) */ { 0x820ce000, 0x421c00, 0x00200 }, /* WF_LMAC_TOP (WF_SEC) */ { 0x820e7000, 0x421e00, 0x00200 }, /* WF_LMAC_TOP BN0 (WF_DMA) */ { 0x820cf000, 0x422000, 0x01000 }, /* WF_LMAC_TOP (WF_PF) */ { 0x820e9000, 0x423400, 0x00200 }, /* WF_LMAC_TOP BN0 (WF_WTBLOFF) */ { 0x820ea000, 0x424000, 0x00200 }, /* WF_LMAC_TOP BN0 (WF_ETBF) */ { 0x820eb000, 0x424200, 0x00400 }, /* WF_LMAC_TOP BN0 (WF_LPON) */ { 0x820ec000, 0x424600, 0x00200 }, /* WF_LMAC_TOP BN0 (WF_INT) */ { 0x820ed000, 0x424800, 0x00800 }, /* WF_LMAC_TOP BN0 (WF_MIB) */ { 0x820ca000, 0x426000, 0x02000 }, /* WF_LMAC_TOP BN0 (WF_MUCOP) */ { 0x820d0000, 0x430000, 0x10000 }, /* WF_LMAC_TOP (WF_WTBLON) */ { 0x00400000, 0x480000, 0x10000 }, /* WF_MCU_SYSRAM */ { 0x00410000, 0x490000, 0x10000 }, /* WF_MCU_SYSRAM */ { 0x820f0000, 0x4a0000, 0x00400 }, /* WF_LMAC_TOP BN1 (WF_CFG) */ { 0x820f1000, 0x4a0600, 0x00200 }, /* WF_LMAC_TOP BN1 (WF_TRB) */ { 0x820f2000, 0x4a0800, 0x00400 }, /* WF_LMAC_TOP BN1 (WF_AGG) */ { 0x820f3000, 0x4a0c00, 0x00400 }, /* WF_LMAC_TOP BN1 (WF_ARB) */ { 0x820f4000, 0x4a1000, 0x00400 }, /* WF_LMAC_TOP BN1 (WF_TMAC) */ { 0x820f5000, 0x4a1400, 0x00800 }, /* WF_LMAC_TOP BN1 (WF_RMAC) */ { 0x820f7000, 0x4a1e00, 0x00200 }, /* WF_LMAC_TOP BN1 (WF_DMA) */ { 0x820f9000, 0x4a3400, 0x00200 }, /* WF_LMAC_TOP BN1 (WF_WTBLOFF) */ { 0x820fa000, 0x4a4000, 0x00200 }, /* WF_LMAC_TOP BN1 (WF_ETBF) */ { 0x820fb000, 0x4a4200, 0x00400 }, /* WF_LMAC_TOP BN1 (WF_LPON) */ { 0x820fc000, 0x4a4600, 0x00200 }, /* WF_LMAC_TOP BN1 (WF_INT) */ { 0x820fd000, 0x4a4800, 0x00800 }, /* WF_LMAC_TOP BN1 (WF_MIB) */ { 0x820c4000, 0x4a8000, 0x01000 }, /* WF_LMAC_TOP (WF_UWTBL ) */ { 0x820b0000, 0x4ae000, 0x01000 }, /* [APB2] WFSYS_ON */ { 0x80020000, 0x4b0000, 0x10000 }, /* WF_TOP_MISC_OFF */ { 0x81020000, 0x4c0000, 0x10000 }, /* WF_TOP_MISC_ON */ { 0x89000000, 0x4d0000, 0x01000 }, /* WF_MCU_CFG_ON */ { 0x89010000, 0x4d1000, 0x01000 }, /* WF_MCU_CIRQ */ { 0x89020000, 0x4d2000, 0x01000 }, /* WF_MCU_GPT */ { 0x89030000, 0x4d3000, 0x01000 }, /* WF_MCU_WDT */ { 0x80010000, 0x4d4000, 0x01000 }, /* WF_AXIDMA */ { 0x0, 0x0, 0x0 }, /* imply end of search */ }; static u32 mt7915_reg_map_l1(struct mt7915_dev *dev, u32 addr) { u32 offset = FIELD_GET(MT_HIF_REMAP_L1_OFFSET, addr); u32 base = FIELD_GET(MT_HIF_REMAP_L1_BASE, addr); u32 l1_remap; if (is_mt798x(&dev->mt76)) return MT_CONN_INFRA_OFFSET(addr); l1_remap = is_mt7915(&dev->mt76) ? MT_HIF_REMAP_L1 : MT_HIF_REMAP_L1_MT7916; dev->bus_ops->rmw(&dev->mt76, l1_remap, MT_HIF_REMAP_L1_MASK, FIELD_PREP(MT_HIF_REMAP_L1_MASK, base)); /* use read to push write */ dev->bus_ops->rr(&dev->mt76, l1_remap); return MT_HIF_REMAP_BASE_L1 + offset; } static u32 mt7915_reg_map_l2(struct mt7915_dev *dev, u32 addr) { u32 offset, base; if (is_mt7915(&dev->mt76)) { offset = FIELD_GET(MT_HIF_REMAP_L2_OFFSET, addr); base = FIELD_GET(MT_HIF_REMAP_L2_BASE, addr); dev->bus_ops->rmw(&dev->mt76, MT_HIF_REMAP_L2, MT_HIF_REMAP_L2_MASK, FIELD_PREP(MT_HIF_REMAP_L2_MASK, base)); /* use read to push write */ dev->bus_ops->rr(&dev->mt76, MT_HIF_REMAP_L2); } else { u32 ofs = is_mt798x(&dev->mt76) ? 0x400000 : 0; offset = FIELD_GET(MT_HIF_REMAP_L2_OFFSET_MT7916, addr); base = FIELD_GET(MT_HIF_REMAP_L2_BASE_MT7916, addr); dev->bus_ops->rmw(&dev->mt76, MT_HIF_REMAP_L2_MT7916 + ofs, MT_HIF_REMAP_L2_MASK_MT7916, FIELD_PREP(MT_HIF_REMAP_L2_MASK_MT7916, base)); /* use read to push write */ dev->bus_ops->rr(&dev->mt76, MT_HIF_REMAP_L2_MT7916 + ofs); offset += (MT_HIF_REMAP_BASE_L2_MT7916 + ofs); } return offset; } static u32 __mt7915_reg_addr(struct mt7915_dev *dev, u32 addr) { int i; if (addr < 0x100000) return addr; if (!dev->reg.map) { dev_err(dev->mt76.dev, "err: reg_map is null\n"); return addr; } for (i = 0; i < dev->reg.map_size; i++) { u32 ofs; if (addr < dev->reg.map[i].phys) continue; ofs = addr - dev->reg.map[i].phys; if (ofs >= dev->reg.map[i].size) continue; return dev->reg.map[i].maps + ofs; } return 0; } static u32 __mt7915_reg_remap_addr(struct mt7915_dev *dev, u32 addr) { if ((addr >= MT_INFRA_BASE && addr < MT_WFSYS0_PHY_START) || (addr >= MT_WFSYS0_PHY_START && addr < MT_WFSYS1_PHY_START) || (addr >= MT_WFSYS1_PHY_START && addr <= MT_WFSYS1_PHY_END)) return mt7915_reg_map_l1(dev, addr); if (dev_is_pci(dev->mt76.dev) && ((addr >= MT_CBTOP1_PHY_START && addr <= MT_CBTOP1_PHY_END) || addr >= MT_CBTOP2_PHY_START)) return mt7915_reg_map_l1(dev, addr); /* CONN_INFRA: covert to phyiscal addr and use layer 1 remap */ if (addr >= MT_INFRA_MCU_START && addr <= MT_INFRA_MCU_END) { addr = addr - MT_INFRA_MCU_START + MT_INFRA_BASE; return mt7915_reg_map_l1(dev, addr); } return mt7915_reg_map_l2(dev, addr); } void mt7915_memcpy_fromio(struct mt7915_dev *dev, void *buf, u32 offset, size_t len) { u32 addr = __mt7915_reg_addr(dev, offset); if (addr) { #if defined(__linux__) memcpy_fromio(buf, dev->mt76.mmio.regs + addr, len); #elif defined(__FreeBSD__) memcpy_fromio(buf, (u8 *)dev->mt76.mmio.regs + addr, len); #endif return; } spin_lock_bh(&dev->reg_lock); #if defined(__linux__) memcpy_fromio(buf, dev->mt76.mmio.regs + #elif defined(__FreeBSD__) memcpy_fromio(buf, (u8 *)dev->mt76.mmio.regs + #endif __mt7915_reg_remap_addr(dev, offset), len); spin_unlock_bh(&dev->reg_lock); } static u32 mt7915_rr(struct mt76_dev *mdev, u32 offset) { struct mt7915_dev *dev = container_of(mdev, struct mt7915_dev, mt76); u32 addr = __mt7915_reg_addr(dev, offset), val; if (addr) return dev->bus_ops->rr(mdev, addr); spin_lock_bh(&dev->reg_lock); val = dev->bus_ops->rr(mdev, __mt7915_reg_remap_addr(dev, offset)); spin_unlock_bh(&dev->reg_lock); return val; } static void mt7915_wr(struct mt76_dev *mdev, u32 offset, u32 val) { struct mt7915_dev *dev = container_of(mdev, struct mt7915_dev, mt76); u32 addr = __mt7915_reg_addr(dev, offset); if (addr) { dev->bus_ops->wr(mdev, addr, val); return; } spin_lock_bh(&dev->reg_lock); dev->bus_ops->wr(mdev, __mt7915_reg_remap_addr(dev, offset), val); spin_unlock_bh(&dev->reg_lock); } static u32 mt7915_rmw(struct mt76_dev *mdev, u32 offset, u32 mask, u32 val) { struct mt7915_dev *dev = container_of(mdev, struct mt7915_dev, mt76); u32 addr = __mt7915_reg_addr(dev, offset); if (addr) return dev->bus_ops->rmw(mdev, addr, mask, val); spin_lock_bh(&dev->reg_lock); val = dev->bus_ops->rmw(mdev, __mt7915_reg_remap_addr(dev, offset), mask, val); spin_unlock_bh(&dev->reg_lock); return val; } #ifdef CONFIG_NET_MEDIATEK_SOC_WED static void mt7915_mmio_wed_update_rx_stats(struct mtk_wed_device *wed, struct mtk_wed_wo_rx_stats *stats) { int idx = le16_to_cpu(stats->wlan_idx); struct mt7915_dev *dev; struct mt76_wcid *wcid; dev = container_of(wed, struct mt7915_dev, mt76.mmio.wed); rcu_read_lock(); wcid = mt76_wcid_ptr(dev, idx); if (wcid) { wcid->stats.rx_bytes += le32_to_cpu(stats->rx_byte_cnt); wcid->stats.rx_packets += le32_to_cpu(stats->rx_pkt_cnt); wcid->stats.rx_errors += le32_to_cpu(stats->rx_err_cnt); wcid->stats.rx_drops += le32_to_cpu(stats->rx_drop_cnt); } rcu_read_unlock(); } static int mt7915_mmio_wed_reset(struct mtk_wed_device *wed) { struct mt76_dev *mdev = container_of(wed, struct mt76_dev, mmio.wed); struct mt7915_dev *dev = container_of(mdev, struct mt7915_dev, mt76); struct mt76_phy *mphy = &dev->mphy; int ret; ASSERT_RTNL(); if (test_and_set_bit(MT76_STATE_WED_RESET, &mphy->state)) return -EBUSY; ret = mt7915_mcu_set_ser(dev, SER_RECOVER, SER_SET_RECOVER_L1, mphy->band_idx); if (ret) goto out; rtnl_unlock(); if (!wait_for_completion_timeout(&mdev->mmio.wed_reset, 20 * HZ)) { dev_err(mdev->dev, "wed reset timeout\n"); ret = -ETIMEDOUT; } rtnl_lock(); out: clear_bit(MT76_STATE_WED_RESET, &mphy->state); return ret; } #endif int mt7915_mmio_wed_init(struct mt7915_dev *dev, void *pdev_ptr, bool pci, int *irq) { #ifdef CONFIG_NET_MEDIATEK_SOC_WED struct mtk_wed_device *wed = &dev->mt76.mmio.wed; int ret; if (!wed_enable) return 0; if (pci) { struct pci_dev *pci_dev = pdev_ptr; wed->wlan.pci_dev = pci_dev; wed->wlan.bus_type = MTK_WED_BUS_PCIE; wed->wlan.base = devm_ioremap(dev->mt76.dev, pci_resource_start(pci_dev, 0), pci_resource_len(pci_dev, 0)); if (!wed->wlan.base) return -ENOMEM; wed->wlan.phy_base = pci_resource_start(pci_dev, 0); wed->wlan.wpdma_int = pci_resource_start(pci_dev, 0) + MT_INT_WED_SOURCE_CSR; wed->wlan.wpdma_mask = pci_resource_start(pci_dev, 0) + MT_INT_WED_MASK_CSR; wed->wlan.wpdma_phys = pci_resource_start(pci_dev, 0) + MT_WFDMA_EXT_CSR_BASE; wed->wlan.wpdma_tx = pci_resource_start(pci_dev, 0) + MT_TXQ_WED_RING_BASE; wed->wlan.wpdma_txfree = pci_resource_start(pci_dev, 0) + MT_RXQ_WED_RING_BASE; wed->wlan.wpdma_rx_glo = pci_resource_start(pci_dev, 0) + MT_WPDMA_GLO_CFG; wed->wlan.wpdma_rx = pci_resource_start(pci_dev, 0) + MT_RXQ_WED_DATA_RING_BASE; } else { struct platform_device *plat_dev = pdev_ptr; struct resource *res; res = platform_get_resource(plat_dev, IORESOURCE_MEM, 0); if (!res) return 0; wed->wlan.platform_dev = plat_dev; wed->wlan.bus_type = MTK_WED_BUS_AXI; wed->wlan.base = devm_ioremap(dev->mt76.dev, res->start, resource_size(res)); if (!wed->wlan.base) return -ENOMEM; wed->wlan.phy_base = res->start; wed->wlan.wpdma_int = res->start + MT_INT_SOURCE_CSR; wed->wlan.wpdma_mask = res->start + MT_INT_MASK_CSR; wed->wlan.wpdma_tx = res->start + MT_TXQ_WED_RING_BASE; wed->wlan.wpdma_txfree = res->start + MT_RXQ_WED_RING_BASE; wed->wlan.wpdma_rx_glo = res->start + MT_WPDMA_GLO_CFG; wed->wlan.wpdma_rx = res->start + MT_RXQ_WED_DATA_RING_BASE; } wed->wlan.nbuf = MT7915_HW_TOKEN_SIZE; wed->wlan.tx_tbit[0] = is_mt7915(&dev->mt76) ? 4 : 30; wed->wlan.tx_tbit[1] = is_mt7915(&dev->mt76) ? 5 : 31; wed->wlan.txfree_tbit = is_mt798x(&dev->mt76) ? 2 : 1; wed->wlan.token_start = MT7915_TOKEN_SIZE - wed->wlan.nbuf; wed->wlan.wcid_512 = !is_mt7915(&dev->mt76); wed->wlan.rx_nbuf = 65536; wed->wlan.rx_npkt = MT7915_WED_RX_TOKEN_SIZE; wed->wlan.rx_size = SKB_WITH_OVERHEAD(MT_RX_BUF_SIZE); if (is_mt7915(&dev->mt76)) { wed->wlan.rx_tbit[0] = 16; wed->wlan.rx_tbit[1] = 17; } else if (is_mt798x(&dev->mt76)) { wed->wlan.rx_tbit[0] = 22; wed->wlan.rx_tbit[1] = 23; } else { wed->wlan.rx_tbit[0] = 18; wed->wlan.rx_tbit[1] = 19; } wed->wlan.init_buf = mt7915_wed_init_buf; wed->wlan.offload_enable = mt76_wed_offload_enable; wed->wlan.offload_disable = mt76_wed_offload_disable; wed->wlan.init_rx_buf = mt76_wed_init_rx_buf; wed->wlan.release_rx_buf = mt76_wed_release_rx_buf; wed->wlan.update_wo_rx_stats = mt7915_mmio_wed_update_rx_stats; wed->wlan.reset = mt7915_mmio_wed_reset; wed->wlan.reset_complete = mt76_wed_reset_complete; dev->mt76.rx_token_size = wed->wlan.rx_npkt; if (mtk_wed_device_attach(wed)) return 0; *irq = wed->irq; dev->mt76.dma_dev = wed->dev; ret = dma_set_mask(wed->dev, DMA_BIT_MASK(32)); if (ret) return ret; return 1; #else return 0; #endif } static int mt7915_mmio_init(struct mt76_dev *mdev, void __iomem *mem_base, u32 device_id) { struct mt76_bus_ops *bus_ops; struct mt7915_dev *dev; dev = container_of(mdev, struct mt7915_dev, mt76); mt76_mmio_init(&dev->mt76, mem_base); spin_lock_init(&dev->reg_lock); switch (device_id) { case 0x7915: dev->reg.reg_rev = mt7915_reg; dev->reg.offs_rev = mt7915_offs; dev->reg.map = mt7915_reg_map; dev->reg.map_size = ARRAY_SIZE(mt7915_reg_map); break; case 0x7906: dev->reg.reg_rev = mt7916_reg; dev->reg.offs_rev = mt7916_offs; dev->reg.map = mt7916_reg_map; dev->reg.map_size = ARRAY_SIZE(mt7916_reg_map); break; case 0x7981: case 0x7986: dev->reg.reg_rev = mt7986_reg; dev->reg.offs_rev = mt7916_offs; dev->reg.map = mt7986_reg_map; dev->reg.map_size = ARRAY_SIZE(mt7986_reg_map); break; default: return -EINVAL; } dev->bus_ops = dev->mt76.bus; bus_ops = devm_kmemdup(dev->mt76.dev, dev->bus_ops, sizeof(*bus_ops), GFP_KERNEL); if (!bus_ops) return -ENOMEM; bus_ops->rr = mt7915_rr; bus_ops->wr = mt7915_wr; bus_ops->rmw = mt7915_rmw; dev->mt76.bus = bus_ops; mdev->rev = (device_id << 16) | (mt76_rr(dev, MT_HW_REV) & 0xff); dev_dbg(mdev->dev, "ASIC revision: %04x\n", mdev->rev); return 0; } void mt7915_dual_hif_set_irq_mask(struct mt7915_dev *dev, bool write_reg, u32 clear, u32 set) { struct mt76_dev *mdev = &dev->mt76; unsigned long flags; spin_lock_irqsave(&mdev->mmio.irq_lock, flags); mdev->mmio.irqmask &= ~clear; mdev->mmio.irqmask |= set; if (write_reg) { if (mtk_wed_device_active(&mdev->mmio.wed)) mtk_wed_device_irq_set_mask(&mdev->mmio.wed, mdev->mmio.irqmask); else mt76_wr(dev, MT_INT_MASK_CSR, mdev->mmio.irqmask); mt76_wr(dev, MT_INT1_MASK_CSR, mdev->mmio.irqmask); } spin_unlock_irqrestore(&mdev->mmio.irq_lock, flags); } static void mt7915_rx_poll_complete(struct mt76_dev *mdev, enum mt76_rxq_id q) { struct mt7915_dev *dev = container_of(mdev, struct mt7915_dev, mt76); mt7915_irq_enable(dev, MT_INT_RX(q)); } /* TODO: support 2/4/6/8 MSI-X vectors */ static void mt7915_irq_tasklet(struct tasklet_struct *t) { struct mt7915_dev *dev = from_tasklet(dev, t, mt76.irq_tasklet); struct mtk_wed_device *wed = &dev->mt76.mmio.wed; u32 intr, intr1, mask; if (mtk_wed_device_active(wed)) { mtk_wed_device_irq_set_mask(wed, 0); if (dev->hif2) mt76_wr(dev, MT_INT1_MASK_CSR, 0); intr = mtk_wed_device_irq_get(wed, dev->mt76.mmio.irqmask); } else { mt76_wr(dev, MT_INT_MASK_CSR, 0); if (dev->hif2) mt76_wr(dev, MT_INT1_MASK_CSR, 0); intr = mt76_rr(dev, MT_INT_SOURCE_CSR); intr &= dev->mt76.mmio.irqmask; mt76_wr(dev, MT_INT_SOURCE_CSR, intr); } if (dev->hif2) { intr1 = mt76_rr(dev, MT_INT1_SOURCE_CSR); intr1 &= dev->mt76.mmio.irqmask; mt76_wr(dev, MT_INT1_SOURCE_CSR, intr1); intr |= intr1; } trace_dev_irq(&dev->mt76, intr, dev->mt76.mmio.irqmask); mask = intr & MT_INT_RX_DONE_ALL; if (intr & MT_INT_TX_DONE_MCU) mask |= MT_INT_TX_DONE_MCU; mt7915_irq_disable(dev, mask); if (intr & MT_INT_TX_DONE_MCU) napi_schedule(&dev->mt76.tx_napi); if (intr & MT_INT_RX(MT_RXQ_MAIN)) napi_schedule(&dev->mt76.napi[MT_RXQ_MAIN]); if (intr & MT_INT_RX(MT_RXQ_BAND1)) napi_schedule(&dev->mt76.napi[MT_RXQ_BAND1]); if (intr & MT_INT_RX(MT_RXQ_MCU)) napi_schedule(&dev->mt76.napi[MT_RXQ_MCU]); if (intr & MT_INT_RX(MT_RXQ_MCU_WA)) napi_schedule(&dev->mt76.napi[MT_RXQ_MCU_WA]); if (!is_mt7915(&dev->mt76) && (intr & MT_INT_RX(MT_RXQ_MAIN_WA))) napi_schedule(&dev->mt76.napi[MT_RXQ_MAIN_WA]); if (intr & MT_INT_RX(MT_RXQ_BAND1_WA)) napi_schedule(&dev->mt76.napi[MT_RXQ_BAND1_WA]); if (intr & MT_INT_MCU_CMD) { u32 val = mt76_rr(dev, MT_MCU_CMD); mt76_wr(dev, MT_MCU_CMD, val); if (val & (MT_MCU_CMD_ERROR_MASK | MT_MCU_CMD_WDT_MASK)) { dev->recovery.state = val; mt7915_reset(dev); } } } irqreturn_t mt7915_irq_handler(int irq, void *dev_instance) { struct mt7915_dev *dev = dev_instance; struct mtk_wed_device *wed = &dev->mt76.mmio.wed; if (mtk_wed_device_active(wed)) mtk_wed_device_irq_set_mask(wed, 0); else mt76_wr(dev, MT_INT_MASK_CSR, 0); if (dev->hif2) mt76_wr(dev, MT_INT1_MASK_CSR, 0); if (!test_bit(MT76_STATE_INITIALIZED, &dev->mphy.state)) return IRQ_NONE; tasklet_schedule(&dev->mt76.irq_tasklet); return IRQ_HANDLED; } struct mt7915_dev *mt7915_mmio_probe(struct device *pdev, void __iomem *mem_base, u32 device_id) { static const struct mt76_driver_ops drv_ops = { /* txwi_size = txd size + txp size */ .txwi_size = MT_TXD_SIZE + sizeof(struct mt76_connac_fw_txp), .drv_flags = MT_DRV_TXWI_NO_FREE | MT_DRV_HW_MGMT_TXQ | MT_DRV_AMSDU_OFFLOAD, .survey_flags = SURVEY_INFO_TIME_TX | SURVEY_INFO_TIME_RX | SURVEY_INFO_TIME_BSS_RX, .token_size = MT7915_TOKEN_SIZE, .tx_prepare_skb = mt7915_tx_prepare_skb, .tx_complete_skb = mt76_connac_tx_complete_skb, .rx_skb = mt7915_queue_rx_skb, .rx_check = mt7915_rx_check, .rx_poll_complete = mt7915_rx_poll_complete, .sta_add = mt7915_mac_sta_add, .sta_event = mt7915_mac_sta_event, .sta_remove = mt7915_mac_sta_remove, .update_survey = mt7915_update_channel, .set_channel = mt7915_set_channel, }; struct mt7915_dev *dev; struct mt76_dev *mdev; int ret; mdev = mt76_alloc_device(pdev, sizeof(*dev), &mt7915_ops, &drv_ops); if (!mdev) return ERR_PTR(-ENOMEM); dev = container_of(mdev, struct mt7915_dev, mt76); ret = mt7915_mmio_init(mdev, mem_base, device_id); if (ret) goto error; tasklet_setup(&mdev->irq_tasklet, mt7915_irq_tasklet); return dev; error: mt76_free_device(&dev->mt76); return ERR_PTR(ret); } static int __init mt7915_init(void) { int ret; ret = pci_register_driver(&mt7915_hif_driver); if (ret) return ret; ret = pci_register_driver(&mt7915_pci_driver); if (ret) goto error_pci; if (IS_ENABLED(CONFIG_MT798X_WMAC)) { ret = platform_driver_register(&mt798x_wmac_driver); if (ret) goto error_wmac; } return 0; error_wmac: pci_unregister_driver(&mt7915_pci_driver); error_pci: pci_unregister_driver(&mt7915_hif_driver); return ret; } static void __exit mt7915_exit(void) { if (IS_ENABLED(CONFIG_MT798X_WMAC)) platform_driver_unregister(&mt798x_wmac_driver); pci_unregister_driver(&mt7915_pci_driver); pci_unregister_driver(&mt7915_hif_driver); } module_init(mt7915_init); module_exit(mt7915_exit); MODULE_DESCRIPTION("MediaTek MT7915E MMIO helpers"); MODULE_LICENSE("Dual BSD/GPL"); diff --git a/sys/contrib/dev/mediatek/mt76/mt7921/mcu.c b/sys/contrib/dev/mediatek/mt76/mt7921/mcu.c index 4b95d7c0a225..57b9de7a6659 100644 --- a/sys/contrib/dev/mediatek/mt76/mt7921/mcu.c +++ b/sys/contrib/dev/mediatek/mt76/mt7921/mcu.c @@ -1,1501 +1,1505 @@ // SPDX-License-Identifier: ISC /* Copyright (C) 2020 MediaTek Inc. */ +#if defined(__FreeBSD__) +#define LINUXKPI_PARAM_PREFIX mt7921_ +#endif + #include #include #include "mt7921.h" #include "mcu.h" #include "../mt76_connac2_mac.h" #include "../mt792x_trace.h" #define MT_STA_BFER BIT(0) #define MT_STA_BFEE BIT(1) static bool mt7921_disable_clc; module_param_named(disable_clc, mt7921_disable_clc, bool, 0644); MODULE_PARM_DESC(disable_clc, "disable CLC support"); int mt7921_mcu_parse_response(struct mt76_dev *mdev, int cmd, struct sk_buff *skb, int seq) { int mcu_cmd = FIELD_GET(__MCU_CMD_FIELD_ID, cmd); struct mt76_connac2_mcu_rxd *rxd; int ret = 0; if (!skb) { dev_err(mdev->dev, "Message %08x (seq %d) timeout\n", cmd, seq); mt792x_reset(mdev); return -ETIMEDOUT; } rxd = (struct mt76_connac2_mcu_rxd *)skb->data; if (seq != rxd->seq) return -EAGAIN; if (cmd == MCU_CMD(PATCH_SEM_CONTROL) || cmd == MCU_CMD(PATCH_FINISH_REQ)) { skb_pull(skb, sizeof(*rxd) - 4); ret = *skb->data; } else if (cmd == MCU_EXT_CMD(THERMAL_CTRL)) { skb_pull(skb, sizeof(*rxd) + 4); ret = le32_to_cpu(*(__le32 *)skb->data); } else if (cmd == MCU_UNI_CMD(DEV_INFO_UPDATE) || cmd == MCU_UNI_CMD(BSS_INFO_UPDATE) || cmd == MCU_UNI_CMD(STA_REC_UPDATE) || cmd == MCU_UNI_CMD(HIF_CTRL) || cmd == MCU_UNI_CMD(OFFLOAD) || cmd == MCU_UNI_CMD(SUSPEND)) { struct mt76_connac_mcu_uni_event *event; skb_pull(skb, sizeof(*rxd)); event = (struct mt76_connac_mcu_uni_event *)skb->data; ret = le32_to_cpu(event->status); /* skip invalid event */ if (mcu_cmd != event->cid) ret = -EAGAIN; } else if (cmd == MCU_CE_QUERY(REG_READ)) { struct mt76_connac_mcu_reg_event *event; skb_pull(skb, sizeof(*rxd)); event = (struct mt76_connac_mcu_reg_event *)skb->data; ret = (int)le32_to_cpu(event->val); } else if (cmd == MCU_EXT_CMD(WF_RF_PIN_CTRL)) { struct mt7921_wf_rf_pin_ctrl_event *event; skb_pull(skb, sizeof(*rxd)); event = (struct mt7921_wf_rf_pin_ctrl_event *)skb->data; ret = (int)event->result; } else { skb_pull(skb, sizeof(struct mt76_connac2_mcu_rxd)); } return ret; } EXPORT_SYMBOL_GPL(mt7921_mcu_parse_response); static int mt7921_mcu_read_eeprom(struct mt792x_dev *dev, u32 offset, u8 *val) { struct mt7921_mcu_eeprom_info *res, req = { .addr = cpu_to_le32(round_down(offset, MT7921_EEPROM_BLOCK_SIZE)), }; struct sk_buff *skb; int ret; ret = mt76_mcu_send_and_get_msg(&dev->mt76, MCU_EXT_QUERY(EFUSE_ACCESS), &req, sizeof(req), true, &skb); if (ret) return ret; res = (struct mt7921_mcu_eeprom_info *)skb->data; *val = res->data[offset % MT7921_EEPROM_BLOCK_SIZE]; dev_kfree_skb(skb); return 0; } #ifdef CONFIG_PM static int mt7921_mcu_set_ipv6_ns_filter(struct mt76_dev *dev, struct ieee80211_vif *vif, bool suspend) { struct mt792x_vif *mvif = (struct mt792x_vif *)vif->drv_priv; struct { struct { u8 bss_idx; u8 pad[3]; } __packed hdr; struct mt76_connac_arpns_tlv arpns; } req = { .hdr = { .bss_idx = mvif->bss_conf.mt76.idx, }, .arpns = { .tag = cpu_to_le16(UNI_OFFLOAD_OFFLOAD_ND), .len = cpu_to_le16(sizeof(struct mt76_connac_arpns_tlv)), .mode = suspend, }, }; return mt76_mcu_send_msg(dev, MCU_UNI_CMD_OFFLOAD, &req, sizeof(req), true); } void mt7921_mcu_set_suspend_iter(void *priv, u8 *mac, struct ieee80211_vif *vif) { if (IS_ENABLED(CONFIG_IPV6)) { struct mt76_phy *phy = priv; mt7921_mcu_set_ipv6_ns_filter(phy->dev, vif, !test_bit(MT76_STATE_RUNNING, &phy->state)); } mt76_connac_mcu_set_suspend_iter(priv, mac, vif); } #endif /* CONFIG_PM */ static void mt7921_mcu_uni_roc_event(struct mt792x_dev *dev, struct sk_buff *skb) { struct mt7921_roc_grant_tlv *grant; struct mt76_connac2_mcu_rxd *rxd; int duration; rxd = (struct mt76_connac2_mcu_rxd *)skb->data; grant = (struct mt7921_roc_grant_tlv *)(rxd->tlv + 4); /* should never happen */ WARN_ON_ONCE((le16_to_cpu(grant->tag) != UNI_EVENT_ROC_GRANT)); if (grant->reqtype == MT7921_ROC_REQ_ROC) ieee80211_ready_on_channel(dev->mt76.phy.hw); dev->phy.roc_grant = true; wake_up(&dev->phy.roc_wait); duration = le32_to_cpu(grant->max_interval); mod_timer(&dev->phy.roc_timer, jiffies + msecs_to_jiffies(duration)); } static void mt7921_mcu_scan_event(struct mt792x_dev *dev, struct sk_buff *skb) { struct mt76_phy *mphy = &dev->mt76.phy; struct mt792x_phy *phy = mphy->priv; spin_lock_bh(&dev->mt76.lock); __skb_queue_tail(&phy->scan_event_list, skb); spin_unlock_bh(&dev->mt76.lock); ieee80211_queue_delayed_work(mphy->hw, &phy->scan_work, MT792x_HW_SCAN_TIMEOUT); } static void mt7921_mcu_connection_loss_iter(void *priv, u8 *mac, struct ieee80211_vif *vif) { struct mt76_vif_link *mvif = (struct mt76_vif_link *)vif->drv_priv; struct mt76_connac_beacon_loss_event *event = priv; if (mvif->idx != event->bss_idx) return; if (!(vif->driver_flags & IEEE80211_VIF_BEACON_FILTER) || vif->type != NL80211_IFTYPE_STATION) return; ieee80211_connection_loss(vif); } static void mt7921_mcu_connection_loss_event(struct mt792x_dev *dev, struct sk_buff *skb) { struct mt76_connac_beacon_loss_event *event; struct mt76_phy *mphy = &dev->mt76.phy; skb_pull(skb, sizeof(struct mt76_connac2_mcu_rxd)); event = (struct mt76_connac_beacon_loss_event *)skb->data; ieee80211_iterate_active_interfaces_atomic(mphy->hw, IEEE80211_IFACE_ITER_RESUME_ALL, mt7921_mcu_connection_loss_iter, event); } static void mt7921_mcu_debug_msg_event(struct mt792x_dev *dev, struct sk_buff *skb) { struct mt7921_debug_msg { __le16 id; u8 type; u8 flag; __le32 value; __le16 len; u8 content[512]; } __packed * msg; skb_pull(skb, sizeof(struct mt76_connac2_mcu_rxd)); msg = (struct mt7921_debug_msg *)skb->data; if (msg->type == 3) { /* fw log */ u16 len = min_t(u16, le16_to_cpu(msg->len), 512); int i; for (i = 0 ; i < len; i++) { if (!msg->content[i]) msg->content[i] = ' '; } wiphy_info(mt76_hw(dev)->wiphy, "%.*s", len, msg->content); } } static void mt7921_mcu_low_power_event(struct mt792x_dev *dev, struct sk_buff *skb) { struct mt7921_mcu_lp_event { u8 state; u8 reserved[3]; } __packed * event; skb_pull(skb, sizeof(struct mt76_connac2_mcu_rxd)); event = (struct mt7921_mcu_lp_event *)skb->data; trace_lp_event(dev, event->state); } static void mt7921_mcu_tx_done_event(struct mt792x_dev *dev, struct sk_buff *skb) { struct mt7921_mcu_tx_done_event *event; skb_pull(skb, sizeof(struct mt76_connac2_mcu_rxd)); event = (struct mt7921_mcu_tx_done_event *)skb->data; mt7921_mac_add_txs(dev, event->txs); } static void mt7921_mcu_rssi_monitor_iter(void *priv, u8 *mac, struct ieee80211_vif *vif) { struct mt792x_vif *mvif = (struct mt792x_vif *)vif->drv_priv; struct mt76_connac_rssi_notify_event *event = priv; enum nl80211_cqm_rssi_threshold_event nl_event; s32 rssi = le32_to_cpu(event->rssi[mvif->bss_conf.mt76.idx]); if (!rssi) return; if (!(vif->driver_flags & IEEE80211_VIF_SUPPORTS_CQM_RSSI)) return; if (rssi > vif->bss_conf.cqm_rssi_thold) nl_event = NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH; else nl_event = NL80211_CQM_RSSI_THRESHOLD_EVENT_LOW; ieee80211_cqm_rssi_notify(vif, nl_event, rssi, GFP_KERNEL); } static void mt7921_mcu_rssi_monitor_event(struct mt792x_dev *dev, struct sk_buff *skb) { struct mt76_connac_rssi_notify_event *event; skb_pull(skb, sizeof(struct mt76_connac2_mcu_rxd)); event = (struct mt76_connac_rssi_notify_event *)skb->data; ieee80211_iterate_active_interfaces_atomic(mt76_hw(dev), IEEE80211_IFACE_ITER_RESUME_ALL, mt7921_mcu_rssi_monitor_iter, event); } static void mt7921_mcu_rx_unsolicited_event(struct mt792x_dev *dev, struct sk_buff *skb) { struct mt76_connac2_mcu_rxd *rxd; rxd = (struct mt76_connac2_mcu_rxd *)skb->data; switch (rxd->eid) { case MCU_EVENT_BSS_BEACON_LOSS: mt7921_mcu_connection_loss_event(dev, skb); break; case MCU_EVENT_SCHED_SCAN_DONE: case MCU_EVENT_SCAN_DONE: mt7921_mcu_scan_event(dev, skb); return; case MCU_EVENT_DBG_MSG: mt7921_mcu_debug_msg_event(dev, skb); break; case MCU_EVENT_COREDUMP: dev->fw_assert = true; mt76_connac_mcu_coredump_event(&dev->mt76, skb, &dev->coredump); return; case MCU_EVENT_LP_INFO: mt7921_mcu_low_power_event(dev, skb); break; case MCU_EVENT_TX_DONE: mt7921_mcu_tx_done_event(dev, skb); break; case MCU_EVENT_RSSI_NOTIFY: mt7921_mcu_rssi_monitor_event(dev, skb); break; default: break; } dev_kfree_skb(skb); } static void mt7921_mcu_uni_rx_unsolicited_event(struct mt792x_dev *dev, struct sk_buff *skb) { struct mt76_connac2_mcu_rxd *rxd; rxd = (struct mt76_connac2_mcu_rxd *)skb->data; switch (rxd->eid) { case MCU_UNI_EVENT_ROC: mt7921_mcu_uni_roc_event(dev, skb); break; default: break; } dev_kfree_skb(skb); } void mt7921_mcu_rx_event(struct mt792x_dev *dev, struct sk_buff *skb) { struct mt76_connac2_mcu_rxd *rxd; if (skb_linearize(skb)) return; rxd = (struct mt76_connac2_mcu_rxd *)skb->data; if (rxd->option & MCU_UNI_CMD_UNSOLICITED_EVENT) { mt7921_mcu_uni_rx_unsolicited_event(dev, skb); return; } if (rxd->eid == 0x6) { mt76_mcu_rx_event(&dev->mt76, skb); return; } if (rxd->ext_eid == MCU_EXT_EVENT_RATE_REPORT || rxd->eid == MCU_EVENT_BSS_BEACON_LOSS || rxd->eid == MCU_EVENT_SCHED_SCAN_DONE || rxd->eid == MCU_EVENT_RSSI_NOTIFY || rxd->eid == MCU_EVENT_SCAN_DONE || rxd->eid == MCU_EVENT_TX_DONE || rxd->eid == MCU_EVENT_DBG_MSG || rxd->eid == MCU_EVENT_COREDUMP || rxd->eid == MCU_EVENT_LP_INFO || !rxd->seq) mt7921_mcu_rx_unsolicited_event(dev, skb); else mt76_mcu_rx_event(&dev->mt76, skb); } /** starec & wtbl **/ int mt7921_mcu_uni_tx_ba(struct mt792x_dev *dev, struct ieee80211_ampdu_params *params, bool enable) { struct mt792x_sta *msta = (struct mt792x_sta *)params->sta->drv_priv; if (enable && !params->amsdu) msta->deflink.wcid.amsdu = false; return mt76_connac_mcu_sta_ba(&dev->mt76, &msta->vif->bss_conf.mt76, params, MCU_UNI_CMD(STA_REC_UPDATE), enable, true); } int mt7921_mcu_uni_rx_ba(struct mt792x_dev *dev, struct ieee80211_ampdu_params *params, bool enable) { struct mt792x_sta *msta = (struct mt792x_sta *)params->sta->drv_priv; return mt76_connac_mcu_sta_ba(&dev->mt76, &msta->vif->bss_conf.mt76, params, MCU_UNI_CMD(STA_REC_UPDATE), enable, false); } static int mt7921_load_clc(struct mt792x_dev *dev, const char *fw_name) { const struct mt76_connac2_fw_trailer *hdr; const struct mt76_connac2_fw_region *region; const struct mt7921_clc *clc; struct mt76_dev *mdev = &dev->mt76; struct mt792x_phy *phy = &dev->phy; const struct firmware *fw; int ret, i, len, offset = 0; #if defined(__linux__) u8 *clc_base = NULL, hw_encap = 0; #elif defined(__FreeBSD__) const u8 *clc_base = NULL; u8 hw_encap = 0; #endif dev->phy.clc_chan_conf = 0xff; if (mt7921_disable_clc || mt76_is_usb(&dev->mt76)) return 0; if (mt76_is_mmio(&dev->mt76)) { ret = mt7921_mcu_read_eeprom(dev, MT_EE_HW_TYPE, &hw_encap); if (ret) return ret; hw_encap = u8_get_bits(hw_encap, MT_EE_HW_TYPE_ENCAP); } ret = request_firmware(&fw, fw_name, mdev->dev); if (ret) return ret; if (!fw || !fw->data || fw->size < sizeof(*hdr)) { dev_err(mdev->dev, "Invalid firmware\n"); ret = -EINVAL; goto out; } hdr = (const void *)(fw->data + fw->size - sizeof(*hdr)); for (i = 0; i < hdr->n_region; i++) { region = (const void *)((const u8 *)hdr - (hdr->n_region - i) * sizeof(*region)); len = le32_to_cpu(region->len); /* check if we have valid buffer size */ if (offset + len > fw->size) { dev_err(mdev->dev, "Invalid firmware region\n"); ret = -EINVAL; goto out; } if ((region->feature_set & FW_FEATURE_NON_DL) && region->type == FW_TYPE_CLC) { #if defined(__linux__) clc_base = (u8 *)(fw->data + offset); #elif defined(__FreeBSD__) clc_base = (const u8 *)(fw->data + offset); #endif break; } offset += len; } if (!clc_base) goto out; for (offset = 0; offset < len; offset += le32_to_cpu(clc->len)) { clc = (const struct mt7921_clc *)(clc_base + offset); /* do not init buf again if chip reset triggered */ if (phy->clc[clc->idx]) continue; /* header content sanity */ if (clc->idx == MT7921_CLC_POWER && u8_get_bits(clc->type, MT_EE_HW_TYPE_ENCAP) != hw_encap) continue; phy->clc[clc->idx] = devm_kmemdup(mdev->dev, clc, le32_to_cpu(clc->len), GFP_KERNEL); if (!phy->clc[clc->idx]) { ret = -ENOMEM; goto out; } } ret = mt7921_mcu_set_clc(dev, "00", ENVIRON_INDOOR); out: release_firmware(fw); return ret; } static void mt7921_mcu_parse_tx_resource(struct mt76_dev *dev, struct sk_buff *skb) { struct mt76_sdio *sdio = &dev->sdio; struct mt7921_tx_resource { __le32 version; __le32 pse_data_quota; __le32 pse_mcu_quota; __le32 ple_data_quota; __le32 ple_mcu_quota; __le16 pse_page_size; __le16 ple_page_size; u8 pp_padding; u8 pad[3]; } __packed * tx_res; tx_res = (struct mt7921_tx_resource *)skb->data; sdio->sched.pse_data_quota = le32_to_cpu(tx_res->pse_data_quota); sdio->pse_mcu_quota_max = le32_to_cpu(tx_res->pse_mcu_quota); /* The mcu quota usage of this function itself must be taken into consideration */ sdio->sched.pse_mcu_quota = sdio->sched.pse_mcu_quota ? sdio->pse_mcu_quota_max : sdio->pse_mcu_quota_max - 1; sdio->sched.ple_data_quota = le32_to_cpu(tx_res->ple_data_quota); sdio->sched.pse_page_size = le16_to_cpu(tx_res->pse_page_size); sdio->sched.deficit = tx_res->pp_padding; } static void mt7921_mcu_parse_phy_cap(struct mt76_dev *dev, struct sk_buff *skb) { struct mt7921_phy_cap { u8 ht; u8 vht; u8 _5g; u8 max_bw; u8 nss; u8 dbdc; u8 tx_ldpc; u8 rx_ldpc; u8 tx_stbc; u8 rx_stbc; u8 hw_path; u8 he; } __packed * cap; enum { WF0_24G, WF0_5G }; cap = (struct mt7921_phy_cap *)skb->data; dev->phy.antenna_mask = BIT(cap->nss) - 1; dev->phy.chainmask = dev->phy.antenna_mask; dev->phy.cap.has_2ghz = cap->hw_path & BIT(WF0_24G); dev->phy.cap.has_5ghz = cap->hw_path & BIT(WF0_5G); } static int mt7921_mcu_get_nic_capability(struct mt792x_phy *mphy) { struct mt76_connac_cap_hdr { __le16 n_element; u8 rsv[2]; } __packed * hdr; struct sk_buff *skb; struct mt76_phy *phy = mphy->mt76; int ret, i; ret = mt76_mcu_send_and_get_msg(phy->dev, MCU_CE_CMD(GET_NIC_CAPAB), NULL, 0, true, &skb); if (ret) return ret; hdr = (struct mt76_connac_cap_hdr *)skb->data; if (skb->len < sizeof(*hdr)) { ret = -EINVAL; goto out; } skb_pull(skb, sizeof(*hdr)); for (i = 0; i < le16_to_cpu(hdr->n_element); i++) { struct tlv_hdr { __le32 type; __le32 len; } __packed * tlv = (struct tlv_hdr *)skb->data; int len; if (skb->len < sizeof(*tlv)) break; skb_pull(skb, sizeof(*tlv)); len = le32_to_cpu(tlv->len); if (skb->len < len) break; switch (le32_to_cpu(tlv->type)) { case MT_NIC_CAP_6G: phy->cap.has_6ghz = skb->data[0]; break; case MT_NIC_CAP_MAC_ADDR: memcpy(phy->macaddr, (void *)skb->data, ETH_ALEN); break; case MT_NIC_CAP_PHY: mt7921_mcu_parse_phy_cap(phy->dev, skb); break; case MT_NIC_CAP_TX_RESOURCE: if (mt76_is_sdio(phy->dev)) mt7921_mcu_parse_tx_resource(phy->dev, skb); break; case MT_NIC_CAP_CHIP_CAP: memcpy(&mphy->chip_cap, (void *)skb->data, sizeof(u64)); break; default: break; } skb_pull(skb, len); } out: dev_kfree_skb(skb); return ret; } int mt7921_mcu_fw_log_2_host(struct mt792x_dev *dev, u8 ctrl) { struct { u8 ctrl_val; u8 pad[3]; } data = { .ctrl_val = ctrl }; return mt76_mcu_send_msg(&dev->mt76, MCU_CE_CMD(FWLOG_2_HOST), &data, sizeof(data), false); } int mt7921_run_firmware(struct mt792x_dev *dev) { int err; err = mt792x_load_firmware(dev); if (err) return err; err = mt7921_mcu_get_nic_capability(&dev->phy); if (err) return err; set_bit(MT76_STATE_MCU_RUNNING, &dev->mphy.state); err = mt7921_load_clc(dev, mt792x_ram_name(dev)); if (err) return err; return mt7921_mcu_fw_log_2_host(dev, 1); } EXPORT_SYMBOL_GPL(mt7921_run_firmware); int mt7921_mcu_radio_led_ctrl(struct mt792x_dev *dev, u8 value) { struct { u8 ctrlid; u8 rsv[3]; } __packed req = { .ctrlid = value, }; return mt76_mcu_send_msg(&dev->mt76, MCU_EXT_CMD(ID_RADIO_ON_OFF_CTRL), &req, sizeof(req), false); } EXPORT_SYMBOL_GPL(mt7921_mcu_radio_led_ctrl); int mt7921_mcu_set_tx(struct mt792x_dev *dev, struct ieee80211_vif *vif) { struct mt792x_vif *mvif = (struct mt792x_vif *)vif->drv_priv; struct edca { __le16 cw_min; __le16 cw_max; __le16 txop; __le16 aifs; u8 guardtime; u8 acm; } __packed; struct mt7921_mcu_tx { struct edca edca[IEEE80211_NUM_ACS]; u8 bss_idx; u8 qos; u8 wmm_idx; u8 pad; } __packed req = { .bss_idx = mvif->bss_conf.mt76.idx, .qos = vif->bss_conf.qos, .wmm_idx = mvif->bss_conf.mt76.wmm_idx, }; struct mu_edca { u8 cw_min; u8 cw_max; u8 aifsn; u8 acm; u8 timer; u8 padding[3]; }; struct mt7921_mcu_mu_tx { u8 ver; u8 pad0; __le16 len; u8 bss_idx; u8 qos; u8 wmm_idx; u8 pad1; struct mu_edca edca[IEEE80211_NUM_ACS]; u8 pad3[32]; } __packed req_mu = { .bss_idx = mvif->bss_conf.mt76.idx, .qos = vif->bss_conf.qos, .wmm_idx = mvif->bss_conf.mt76.wmm_idx, }; static const int to_aci[] = { 1, 0, 2, 3 }; int ac, ret; for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) { struct ieee80211_tx_queue_params *q = &mvif->bss_conf.queue_params[ac]; struct edca *e = &req.edca[to_aci[ac]]; e->aifs = cpu_to_le16(q->aifs); e->txop = cpu_to_le16(q->txop); if (q->cw_min) e->cw_min = cpu_to_le16(q->cw_min); else e->cw_min = cpu_to_le16(5); if (q->cw_max) e->cw_max = cpu_to_le16(q->cw_max); else e->cw_max = cpu_to_le16(10); } ret = mt76_mcu_send_msg(&dev->mt76, MCU_CE_CMD(SET_EDCA_PARMS), &req, sizeof(req), false); if (ret) return ret; if (!vif->bss_conf.he_support) return 0; for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) { struct ieee80211_he_mu_edca_param_ac_rec *q; struct mu_edca *e; if (!mvif->bss_conf.queue_params[ac].mu_edca) break; q = &mvif->bss_conf.queue_params[ac].mu_edca_param_rec; e = &(req_mu.edca[to_aci[ac]]); e->cw_min = q->ecw_min_max & 0xf; e->cw_max = (q->ecw_min_max & 0xf0) >> 4; e->aifsn = q->aifsn; e->timer = q->mu_edca_timer; } return mt76_mcu_send_msg(&dev->mt76, MCU_CE_CMD(SET_MU_EDCA_PARMS), &req_mu, sizeof(req_mu), false); } int mt7921_mcu_set_roc(struct mt792x_phy *phy, struct mt792x_vif *vif, struct ieee80211_channel *chan, int duration, enum mt7921_roc_req type, u8 token_id) { int center_ch = ieee80211_frequency_to_channel(chan->center_freq); struct mt792x_dev *dev = phy->dev; struct { struct { u8 rsv[4]; } __packed hdr; struct roc_acquire_tlv { __le16 tag; __le16 len; u8 bss_idx; u8 tokenid; u8 control_channel; u8 sco; u8 band; u8 bw; u8 center_chan; u8 center_chan2; u8 bw_from_ap; u8 center_chan_from_ap; u8 center_chan2_from_ap; u8 reqtype; __le32 maxinterval; u8 dbdcband; u8 rsv[3]; } __packed roc; } __packed req = { .roc = { .tag = cpu_to_le16(UNI_ROC_ACQUIRE), .len = cpu_to_le16(sizeof(struct roc_acquire_tlv)), .tokenid = token_id, .reqtype = type, .maxinterval = cpu_to_le32(duration), .bss_idx = vif->bss_conf.mt76.idx, .control_channel = chan->hw_value, .bw = CMD_CBW_20MHZ, .bw_from_ap = CMD_CBW_20MHZ, .center_chan = center_ch, .center_chan_from_ap = center_ch, .dbdcband = 0xff, /* auto */ }, }; if (chan->hw_value < center_ch) req.roc.sco = 1; /* SCA */ else if (chan->hw_value > center_ch) req.roc.sco = 3; /* SCB */ switch (chan->band) { case NL80211_BAND_6GHZ: req.roc.band = 3; break; case NL80211_BAND_5GHZ: req.roc.band = 2; break; default: req.roc.band = 1; break; } return mt76_mcu_send_msg(&dev->mt76, MCU_UNI_CMD(ROC), &req, sizeof(req), false); } int mt7921_mcu_abort_roc(struct mt792x_phy *phy, struct mt792x_vif *vif, u8 token_id) { struct mt792x_dev *dev = phy->dev; struct { struct { u8 rsv[4]; } __packed hdr; struct roc_abort_tlv { __le16 tag; __le16 len; u8 bss_idx; u8 tokenid; u8 dbdcband; u8 rsv[5]; } __packed abort; } __packed req = { .abort = { .tag = cpu_to_le16(UNI_ROC_ABORT), .len = cpu_to_le16(sizeof(struct roc_abort_tlv)), .tokenid = token_id, .bss_idx = vif->bss_conf.mt76.idx, .dbdcband = 0xff, /* auto*/ }, }; return mt76_mcu_send_msg(&dev->mt76, MCU_UNI_CMD(ROC), &req, sizeof(req), false); } int mt7921_mcu_set_chan_info(struct mt792x_phy *phy, int cmd) { struct mt792x_dev *dev = phy->dev; struct cfg80211_chan_def *chandef = &phy->mt76->chandef; int freq1 = chandef->center_freq1; struct { u8 control_ch; u8 center_ch; u8 bw; u8 tx_streams_num; u8 rx_streams; /* mask or num */ u8 switch_reason; u8 band_idx; u8 center_ch2; /* for 80+80 only */ __le16 cac_case; u8 channel_band; u8 rsv0; __le32 outband_freq; u8 txpower_drop; u8 ap_bw; u8 ap_center_ch; u8 rsv1[57]; } __packed req = { .control_ch = chandef->chan->hw_value, .center_ch = ieee80211_frequency_to_channel(freq1), .bw = mt76_connac_chan_bw(chandef), .tx_streams_num = hweight8(phy->mt76->antenna_mask), .rx_streams = phy->mt76->antenna_mask, .band_idx = phy != &dev->phy, }; if (chandef->chan->band == NL80211_BAND_6GHZ) req.channel_band = 2; else req.channel_band = chandef->chan->band; if (cmd == MCU_EXT_CMD(SET_RX_PATH) || dev->mt76.hw->conf.flags & IEEE80211_CONF_MONITOR) req.switch_reason = CH_SWITCH_NORMAL; else if (phy->mt76->offchannel) req.switch_reason = CH_SWITCH_SCAN_BYPASS_DPD; else if (!cfg80211_reg_can_beacon(dev->mt76.hw->wiphy, chandef, NL80211_IFTYPE_AP)) req.switch_reason = CH_SWITCH_DFS; else req.switch_reason = CH_SWITCH_NORMAL; if (cmd == MCU_EXT_CMD(CHANNEL_SWITCH)) req.rx_streams = hweight8(req.rx_streams); if (chandef->width == NL80211_CHAN_WIDTH_80P80) { int freq2 = chandef->center_freq2; req.center_ch2 = ieee80211_frequency_to_channel(freq2); } return mt76_mcu_send_msg(&dev->mt76, cmd, &req, sizeof(req), true); } int mt7921_mcu_set_eeprom(struct mt792x_dev *dev) { struct req_hdr { u8 buffer_mode; u8 format; __le16 len; } __packed req = { .buffer_mode = EE_MODE_EFUSE, .format = EE_FORMAT_WHOLE, }; return mt76_mcu_send_msg(&dev->mt76, MCU_EXT_CMD(EFUSE_BUFFER_MODE), &req, sizeof(req), true); } EXPORT_SYMBOL_GPL(mt7921_mcu_set_eeprom); int mt7921_mcu_uni_bss_ps(struct mt792x_dev *dev, struct ieee80211_vif *vif) { struct mt792x_vif *mvif = (struct mt792x_vif *)vif->drv_priv; struct { struct { u8 bss_idx; u8 pad[3]; } __packed hdr; struct ps_tlv { __le16 tag; __le16 len; u8 ps_state; /* 0: device awake * 1: static power save * 2: dynamic power saving * 3: enter TWT power saving * 4: leave TWT power saving */ u8 pad[3]; } __packed ps; } __packed ps_req = { .hdr = { .bss_idx = mvif->bss_conf.mt76.idx, }, .ps = { .tag = cpu_to_le16(UNI_BSS_INFO_PS), .len = cpu_to_le16(sizeof(struct ps_tlv)), .ps_state = vif->cfg.ps ? 2 : 0, }, }; if (vif->type != NL80211_IFTYPE_STATION) return -EOPNOTSUPP; return mt76_mcu_send_msg(&dev->mt76, MCU_UNI_CMD(BSS_INFO_UPDATE), &ps_req, sizeof(ps_req), true); } static int mt7921_mcu_uni_bss_bcnft(struct mt792x_dev *dev, struct ieee80211_vif *vif, bool enable) { struct mt792x_vif *mvif = (struct mt792x_vif *)vif->drv_priv; struct { struct { u8 bss_idx; u8 pad[3]; } __packed hdr; struct bcnft_tlv { __le16 tag; __le16 len; __le16 bcn_interval; u8 dtim_period; u8 pad; } __packed bcnft; } __packed bcnft_req = { .hdr = { .bss_idx = mvif->bss_conf.mt76.idx, }, .bcnft = { .tag = cpu_to_le16(UNI_BSS_INFO_BCNFT), .len = cpu_to_le16(sizeof(struct bcnft_tlv)), .bcn_interval = cpu_to_le16(vif->bss_conf.beacon_int), .dtim_period = vif->bss_conf.dtim_period, }, }; if (vif->type != NL80211_IFTYPE_STATION) return 0; return mt76_mcu_send_msg(&dev->mt76, MCU_UNI_CMD(BSS_INFO_UPDATE), &bcnft_req, sizeof(bcnft_req), true); } int mt7921_mcu_set_bss_pm(struct mt792x_dev *dev, struct ieee80211_vif *vif, bool enable) { struct mt792x_vif *mvif = (struct mt792x_vif *)vif->drv_priv; struct { u8 bss_idx; u8 dtim_period; __le16 aid; __le16 bcn_interval; __le16 atim_window; u8 uapsd; u8 bmc_delivered_ac; u8 bmc_triggered_ac; u8 pad; } req = { .bss_idx = mvif->bss_conf.mt76.idx, .aid = cpu_to_le16(vif->cfg.aid), .dtim_period = vif->bss_conf.dtim_period, .bcn_interval = cpu_to_le16(vif->bss_conf.beacon_int), }; struct { u8 bss_idx; u8 pad[3]; } req_hdr = { .bss_idx = mvif->bss_conf.mt76.idx, }; int err; err = mt76_mcu_send_msg(&dev->mt76, MCU_CE_CMD(SET_BSS_ABORT), &req_hdr, sizeof(req_hdr), false); if (err < 0 || !enable) return err; return mt76_mcu_send_msg(&dev->mt76, MCU_CE_CMD(SET_BSS_CONNECTED), &req, sizeof(req), false); } int mt7921_mcu_sta_update(struct mt792x_dev *dev, struct ieee80211_sta *sta, struct ieee80211_vif *vif, bool enable, enum mt76_sta_info_state state) { struct mt792x_vif *mvif = (struct mt792x_vif *)vif->drv_priv; int rssi = -ewma_rssi_read(&mvif->bss_conf.rssi); struct mt76_sta_cmd_info info = { .sta = sta, .vif = vif, .enable = enable, .cmd = MCU_UNI_CMD(STA_REC_UPDATE), .state = state, .offload_fw = true, .rcpi = to_rcpi(rssi), }; struct mt792x_sta *msta; msta = sta ? (struct mt792x_sta *)sta->drv_priv : NULL; info.wcid = msta ? &msta->deflink.wcid : &mvif->sta.deflink.wcid; info.newly = msta ? state != MT76_STA_INFO_STATE_ASSOC : true; return mt76_connac_mcu_sta_cmd(&dev->mphy, &info); } int mt7921_mcu_set_beacon_filter(struct mt792x_dev *dev, struct ieee80211_vif *vif, bool enable) { #define MT7921_FIF_BIT_CLR BIT(1) #define MT7921_FIF_BIT_SET BIT(0) int err; if (enable) { err = mt7921_mcu_uni_bss_bcnft(dev, vif, true); if (err) return err; err = mt7921_mcu_set_rxfilter(dev, 0, MT7921_FIF_BIT_SET, MT_WF_RFCR_DROP_OTHER_BEACON); if (err) return err; return 0; } err = mt7921_mcu_set_bss_pm(dev, vif, false); if (err) return err; err = mt7921_mcu_set_rxfilter(dev, 0, MT7921_FIF_BIT_CLR, MT_WF_RFCR_DROP_OTHER_BEACON); if (err) return err; return 0; } int mt7921_get_txpwr_info(struct mt792x_dev *dev, struct mt7921_txpwr *txpwr) { struct mt7921_txpwr_event *event; struct mt7921_txpwr_req req = { .dbdc_idx = 0, }; struct sk_buff *skb; int ret; ret = mt76_mcu_send_and_get_msg(&dev->mt76, MCU_CE_CMD(GET_TXPWR), &req, sizeof(req), true, &skb); if (ret) return ret; event = (struct mt7921_txpwr_event *)skb->data; WARN_ON(skb->len != le16_to_cpu(event->len)); memcpy(txpwr, &event->txpwr, sizeof(event->txpwr)); dev_kfree_skb(skb); return 0; } int mt7921_mcu_set_sniffer(struct mt792x_dev *dev, struct ieee80211_vif *vif, bool enable) { struct mt76_vif_link *mvif = (struct mt76_vif_link *)vif->drv_priv; struct { struct { u8 band_idx; u8 pad[3]; } __packed hdr; struct sniffer_enable_tlv { __le16 tag; __le16 len; u8 enable; u8 pad[3]; } __packed enable; } req = { .hdr = { .band_idx = mvif->band_idx, }, .enable = { .tag = cpu_to_le16(0), .len = cpu_to_le16(sizeof(struct sniffer_enable_tlv)), .enable = enable, }, }; return mt76_mcu_send_msg(&dev->mt76, MCU_UNI_CMD(SNIFFER), &req, sizeof(req), true); } int mt7921_mcu_config_sniffer(struct mt792x_vif *vif, struct ieee80211_chanctx_conf *ctx) { struct cfg80211_chan_def *chandef = &ctx->def; int freq1 = chandef->center_freq1, freq2 = chandef->center_freq2; static const u8 ch_band[] = { [NL80211_BAND_2GHZ] = 1, [NL80211_BAND_5GHZ] = 2, [NL80211_BAND_6GHZ] = 3, }; static const u8 ch_width[] = { [NL80211_CHAN_WIDTH_20_NOHT] = 0, [NL80211_CHAN_WIDTH_20] = 0, [NL80211_CHAN_WIDTH_40] = 0, [NL80211_CHAN_WIDTH_80] = 1, [NL80211_CHAN_WIDTH_160] = 2, [NL80211_CHAN_WIDTH_80P80] = 3, [NL80211_CHAN_WIDTH_5] = 4, [NL80211_CHAN_WIDTH_10] = 5, [NL80211_CHAN_WIDTH_320] = 6, }; struct { struct { u8 band_idx; u8 pad[3]; } __packed hdr; struct config_tlv { __le16 tag; __le16 len; u16 aid; u8 ch_band; u8 bw; u8 control_ch; u8 sco; u8 center_ch; u8 center_ch2; u8 drop_err; u8 pad[3]; } __packed tlv; } __packed req = { .hdr = { .band_idx = vif->bss_conf.mt76.band_idx, }, .tlv = { .tag = cpu_to_le16(1), .len = cpu_to_le16(sizeof(req.tlv)), .control_ch = chandef->chan->hw_value, .center_ch = ieee80211_frequency_to_channel(freq1), .drop_err = 1, }, }; if (chandef->chan->band < ARRAY_SIZE(ch_band)) req.tlv.ch_band = ch_band[chandef->chan->band]; if (chandef->width < ARRAY_SIZE(ch_width)) req.tlv.bw = ch_width[chandef->width]; if (freq2) req.tlv.center_ch2 = ieee80211_frequency_to_channel(freq2); if (req.tlv.control_ch < req.tlv.center_ch) req.tlv.sco = 1; /* SCA */ else if (req.tlv.control_ch > req.tlv.center_ch) req.tlv.sco = 3; /* SCB */ return mt76_mcu_send_msg(vif->phy->mt76->dev, MCU_UNI_CMD(SNIFFER), &req, sizeof(req), true); } int mt7921_mcu_uni_add_beacon_offload(struct mt792x_dev *dev, struct ieee80211_hw *hw, struct ieee80211_vif *vif, bool enable) { struct mt792x_vif *mvif = (struct mt792x_vif *)vif->drv_priv; struct mt76_wcid *wcid = &dev->mt76.global_wcid; struct ieee80211_mutable_offsets offs; struct { struct req_hdr { u8 bss_idx; u8 pad[3]; } __packed hdr; struct bcn_content_tlv { __le16 tag; __le16 len; __le16 tim_ie_pos; __le16 csa_ie_pos; __le16 bcc_ie_pos; /* 0: disable beacon offload * 1: enable beacon offload * 2: update probe respond offload */ u8 enable; /* 0: legacy format (TXD + payload) * 1: only cap field IE */ u8 type; __le16 pkt_len; u8 pkt[512]; } __packed beacon_tlv; } req = { .hdr = { .bss_idx = mvif->bss_conf.mt76.idx, }, .beacon_tlv = { .tag = cpu_to_le16(UNI_BSS_INFO_BCN_CONTENT), .len = cpu_to_le16(sizeof(struct bcn_content_tlv)), .enable = enable, }, }; struct sk_buff *skb; /* support enable/update process only * disable flow would be handled in bss stop handler automatically */ if (!enable) return -EOPNOTSUPP; skb = ieee80211_beacon_get_template(mt76_hw(dev), vif, &offs, 0); if (!skb) return -EINVAL; if (skb->len > 512 - MT_TXD_SIZE) { dev_err(dev->mt76.dev, "beacon size limit exceed\n"); dev_kfree_skb(skb); return -EINVAL; } mt76_connac2_mac_write_txwi(&dev->mt76, (__le32 *)(req.beacon_tlv.pkt), skb, wcid, NULL, 0, 0, BSS_CHANGED_BEACON); memcpy(req.beacon_tlv.pkt + MT_TXD_SIZE, skb->data, skb->len); req.beacon_tlv.pkt_len = cpu_to_le16(MT_TXD_SIZE + skb->len); req.beacon_tlv.tim_ie_pos = cpu_to_le16(MT_TXD_SIZE + offs.tim_offset); if (offs.cntdwn_counter_offs[0]) { u16 csa_offs; csa_offs = MT_TXD_SIZE + offs.cntdwn_counter_offs[0] - 4; req.beacon_tlv.csa_ie_pos = cpu_to_le16(csa_offs); } dev_kfree_skb(skb); return mt76_mcu_send_msg(&dev->mt76, MCU_UNI_CMD(BSS_INFO_UPDATE), &req, sizeof(req), true); } static int __mt7921_mcu_set_clc(struct mt792x_dev *dev, u8 *alpha2, enum environment_cap env_cap, struct mt7921_clc *clc, u8 idx) { #define CLC_CAP_EVT_EN BIT(0) #define CLC_CAP_DTS_EN BIT(1) struct sk_buff *skb, *ret_skb = NULL; struct { u8 ver; u8 pad0; __le16 len; u8 idx; u8 env; u8 acpi_conf; u8 cap; u8 alpha2[2]; u8 type[2]; u8 env_6g; u8 mtcl_conf; u8 rsvd[62]; } __packed req = { .ver = 1, .idx = idx, .env = env_cap, .env_6g = dev->phy.power_type, .acpi_conf = mt792x_acpi_get_flags(&dev->phy), .mtcl_conf = mt792x_acpi_get_mtcl_conf(&dev->phy, alpha2), }; int ret, valid_cnt = 0; u32 buf_len = 0; u8 *pos; if (!clc) return 0; if (dev->phy.chip_cap & MT792x_CHIP_CAP_CLC_EVT_EN) req.cap |= CLC_CAP_EVT_EN; if (mt76_find_power_limits_node(&dev->mt76)) req.cap |= CLC_CAP_DTS_EN; buf_len = le32_to_cpu(clc->len) - sizeof(*clc); pos = clc->data; while (buf_len > 16) { struct mt7921_clc_rule *rule = (struct mt7921_clc_rule *)pos; u16 len = le16_to_cpu(rule->len); u16 offset = len + sizeof(*rule); pos += offset; buf_len -= offset; if (rule->alpha2[0] != alpha2[0] || rule->alpha2[1] != alpha2[1]) continue; memcpy(req.alpha2, rule->alpha2, 2); memcpy(req.type, rule->type, 2); req.len = cpu_to_le16(sizeof(req) + len); skb = __mt76_mcu_msg_alloc(&dev->mt76, &req, le16_to_cpu(req.len), sizeof(req), GFP_KERNEL); if (!skb) return -ENOMEM; skb_put_data(skb, rule->data, len); ret = mt76_mcu_skb_send_and_get_msg(&dev->mt76, skb, MCU_CE_CMD(SET_CLC), !!(req.cap & CLC_CAP_EVT_EN), &ret_skb); if (ret < 0) return ret; if (ret_skb) { struct mt7921_clc_info_tlv *info; info = (struct mt7921_clc_info_tlv *)(ret_skb->data + 4); dev->phy.clc_chan_conf = info->chan_conf; dev_kfree_skb(ret_skb); } valid_cnt++; } if (!valid_cnt) return -ENOENT; return 0; } int mt7921_mcu_set_clc(struct mt792x_dev *dev, u8 *alpha2, enum environment_cap env_cap) { struct mt792x_phy *phy = (struct mt792x_phy *)&dev->phy; int i, ret; /* submit all clc config */ for (i = 0; i < ARRAY_SIZE(phy->clc); i++) { ret = __mt7921_mcu_set_clc(dev, alpha2, env_cap, phy->clc[i], i); /* If no country found, set "00" as default */ if (ret == -ENOENT) ret = __mt7921_mcu_set_clc(dev, "00", ENVIRON_INDOOR, phy->clc[i], i); if (ret < 0) return ret; } return 0; } int mt7921_mcu_get_temperature(struct mt792x_phy *phy) { struct mt792x_dev *dev = phy->dev; struct { u8 ctrl_id; u8 action; u8 band_idx; u8 rsv[5]; } req = { .ctrl_id = THERMAL_SENSOR_TEMP_QUERY, .band_idx = phy->mt76->band_idx, }; return mt76_mcu_send_msg(&dev->mt76, MCU_EXT_CMD(THERMAL_CTRL), &req, sizeof(req), true); } int mt7921_mcu_wf_rf_pin_ctrl(struct mt792x_phy *phy, u8 action) { struct mt792x_dev *dev = phy->dev; struct { u8 action; u8 value; } req = { .action = action, .value = 0, }; return mt76_mcu_send_msg(&dev->mt76, MCU_EXT_CMD(WF_RF_PIN_CTRL), &req, sizeof(req), action ? true : false); } int mt7921_mcu_set_rxfilter(struct mt792x_dev *dev, u32 fif, u8 bit_op, u32 bit_map) { struct { u8 rsv[4]; u8 mode; u8 rsv2[3]; __le32 fif; __le32 bit_map; /* bit_* for bitmap update */ u8 bit_op; u8 pad[51]; } __packed data = { .mode = fif ? 1 : 2, .fif = cpu_to_le32(fif), .bit_map = cpu_to_le32(bit_map), .bit_op = bit_op, }; return mt76_mcu_send_msg(&dev->mt76, MCU_CE_CMD(SET_RX_FILTER), &data, sizeof(data), false); } int mt7921_mcu_set_rssimonitor(struct mt792x_dev *dev, struct ieee80211_vif *vif) { struct mt792x_vif *mvif = (struct mt792x_vif *)vif->drv_priv; struct { u8 enable; s8 cqm_rssi_high; s8 cqm_rssi_low; u8 bss_idx; u16 duration; u8 rsv2[2]; } __packed data = { .enable = vif->cfg.assoc, .cqm_rssi_high = vif->bss_conf.cqm_rssi_thold + vif->bss_conf.cqm_rssi_hyst, .cqm_rssi_low = vif->bss_conf.cqm_rssi_thold - vif->bss_conf.cqm_rssi_hyst, .bss_idx = mvif->bss_conf.mt76.idx, }; return mt76_mcu_send_msg(&dev->mt76, MCU_CE_CMD(RSSI_MONITOR), &data, sizeof(data), false); } diff --git a/sys/contrib/dev/mediatek/mt76/mt7925/mcu.c b/sys/contrib/dev/mediatek/mt76/mt7925/mcu.c index d62d461db699..77e494a4ece0 100644 --- a/sys/contrib/dev/mediatek/mt76/mt7925/mcu.c +++ b/sys/contrib/dev/mediatek/mt76/mt7925/mcu.c @@ -1,3807 +1,3811 @@ // SPDX-License-Identifier: ISC /* Copyright (C) 2023 MediaTek Inc. */ +#if defined(__FreeBSD__) +#define LINUXKPI_PARAM_PREFIX mt7925_ +#endif + #include #include #include "mt7925.h" #include "mcu.h" #include "mac.h" #define MT_STA_BFER BIT(0) #define MT_STA_BFEE BIT(1) static bool mt7925_disable_clc; module_param_named(disable_clc, mt7925_disable_clc, bool, 0644); MODULE_PARM_DESC(disable_clc, "disable CLC support"); int mt7925_mcu_parse_response(struct mt76_dev *mdev, int cmd, struct sk_buff *skb, int seq) { int mcu_cmd = FIELD_GET(__MCU_CMD_FIELD_ID, cmd); struct mt7925_mcu_rxd *rxd; int ret = 0; if (!skb) { dev_err(mdev->dev, "Message %08x (seq %d) timeout\n", cmd, seq); mt792x_reset(mdev); return -ETIMEDOUT; } rxd = (struct mt7925_mcu_rxd *)skb->data; if (seq != rxd->seq) return -EAGAIN; if (cmd == MCU_CMD(PATCH_SEM_CONTROL) || cmd == MCU_CMD(PATCH_FINISH_REQ)) { skb_pull(skb, sizeof(*rxd) - 4); ret = *skb->data; } else if (cmd == MCU_UNI_CMD(DEV_INFO_UPDATE) || cmd == MCU_UNI_CMD(BSS_INFO_UPDATE) || cmd == MCU_UNI_CMD(STA_REC_UPDATE) || cmd == MCU_UNI_CMD(OFFLOAD) || cmd == MCU_UNI_CMD(SUSPEND)) { struct mt7925_mcu_uni_event *event; skb_pull(skb, sizeof(*rxd)); event = (struct mt7925_mcu_uni_event *)skb->data; ret = le32_to_cpu(event->status); /* skip invalid event */ if (mcu_cmd != event->cid) ret = -EAGAIN; } else { skb_pull(skb, sizeof(*rxd)); } return ret; } EXPORT_SYMBOL_GPL(mt7925_mcu_parse_response); int mt7925_mcu_regval(struct mt792x_dev *dev, u32 regidx, u32 *val, bool set) { #define MT_RF_REG_HDR GENMASK(31, 24) #define MT_RF_REG_ANT GENMASK(23, 16) #define RF_REG_PREFIX 0x99 struct { u8 __rsv[4]; union { struct uni_cmd_access_reg_basic { __le16 tag; __le16 len; __le32 idx; __le32 data; } __packed reg; struct uni_cmd_access_rf_reg_basic { __le16 tag; __le16 len; __le16 ant; u8 __rsv[2]; __le32 idx; __le32 data; } __packed rf_reg; }; } __packed * res, req; struct sk_buff *skb; int ret; if (u32_get_bits(regidx, MT_RF_REG_HDR) == RF_REG_PREFIX) { req.rf_reg.tag = cpu_to_le16(UNI_CMD_ACCESS_RF_REG_BASIC); req.rf_reg.len = cpu_to_le16(sizeof(req.rf_reg)); req.rf_reg.ant = cpu_to_le16(u32_get_bits(regidx, MT_RF_REG_ANT)); req.rf_reg.idx = cpu_to_le32(regidx); req.rf_reg.data = set ? cpu_to_le32(*val) : 0; } else { req.reg.tag = cpu_to_le16(UNI_CMD_ACCESS_REG_BASIC); req.reg.len = cpu_to_le16(sizeof(req.reg)); req.reg.idx = cpu_to_le32(regidx); req.reg.data = set ? cpu_to_le32(*val) : 0; } if (set) return mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(REG_ACCESS), &req, sizeof(req), true); ret = mt76_mcu_send_and_get_msg(&dev->mt76, MCU_WM_UNI_CMD_QUERY(REG_ACCESS), &req, sizeof(req), true, &skb); if (ret) return ret; res = (void *)skb->data; if (u32_get_bits(regidx, MT_RF_REG_HDR) == RF_REG_PREFIX) *val = le32_to_cpu(res->rf_reg.data); else *val = le32_to_cpu(res->reg.data); dev_kfree_skb(skb); return 0; } EXPORT_SYMBOL_GPL(mt7925_mcu_regval); int mt7925_mcu_update_arp_filter(struct mt76_dev *dev, struct ieee80211_bss_conf *link_conf) { struct mt792x_bss_conf *mconf = mt792x_link_conf_to_mconf(link_conf); struct ieee80211_vif *mvif = link_conf->vif; struct sk_buff *skb; int i, len = min_t(int, mvif->cfg.arp_addr_cnt, IEEE80211_BSS_ARP_ADDR_LIST_LEN); struct { struct { u8 bss_idx; u8 pad[3]; } __packed hdr; struct mt7925_arpns_tlv arp; } req = { .hdr = { .bss_idx = mconf->mt76.idx, }, .arp = { .tag = cpu_to_le16(UNI_OFFLOAD_OFFLOAD_ARP), .len = cpu_to_le16(sizeof(req) - 4 + len * 2 * sizeof(__be32)), .ips_num = len, .enable = true, }, }; skb = mt76_mcu_msg_alloc(dev, NULL, sizeof(req) + len * 2 * sizeof(__be32)); if (!skb) return -ENOMEM; skb_put_data(skb, &req, sizeof(req)); for (i = 0; i < len; i++) { skb_put_data(skb, &mvif->cfg.arp_addr_list[i], sizeof(__be32)); skb_put_zero(skb, sizeof(__be32)); } return mt76_mcu_skb_send_msg(dev, skb, MCU_UNI_CMD(OFFLOAD), true); } #ifdef CONFIG_PM static int mt7925_connac_mcu_set_wow_ctrl(struct mt76_phy *phy, struct ieee80211_vif *vif, bool suspend, struct cfg80211_wowlan *wowlan) { struct mt76_vif_link *mvif = (struct mt76_vif_link *)vif->drv_priv; struct ieee80211_scan_ies ies = {}; struct mt76_dev *dev = phy->dev; struct { struct { u8 bss_idx; u8 pad[3]; } __packed hdr; struct mt76_connac_wow_ctrl_tlv wow_ctrl_tlv; struct mt76_connac_wow_gpio_param_tlv gpio_tlv; } req = { .hdr = { .bss_idx = mvif->idx, }, .wow_ctrl_tlv = { .tag = cpu_to_le16(UNI_SUSPEND_WOW_CTRL), .len = cpu_to_le16(sizeof(struct mt76_connac_wow_ctrl_tlv)), .cmd = suspend ? 1 : 2, }, .gpio_tlv = { .tag = cpu_to_le16(UNI_SUSPEND_WOW_GPIO_PARAM), .len = cpu_to_le16(sizeof(struct mt76_connac_wow_gpio_param_tlv)), .gpio_pin = 0xff, /* follow fw about GPIO pin */ }, }; if (wowlan->magic_pkt) req.wow_ctrl_tlv.trigger |= UNI_WOW_DETECT_TYPE_MAGIC; if (wowlan->disconnect) req.wow_ctrl_tlv.trigger |= (UNI_WOW_DETECT_TYPE_DISCONNECT | UNI_WOW_DETECT_TYPE_BCN_LOST); if (wowlan->nd_config) { mt7925_mcu_sched_scan_req(phy, vif, wowlan->nd_config, &ies); req.wow_ctrl_tlv.trigger |= UNI_WOW_DETECT_TYPE_SCH_SCAN_HIT; mt7925_mcu_sched_scan_enable(phy, vif, suspend); } if (wowlan->n_patterns) req.wow_ctrl_tlv.trigger |= UNI_WOW_DETECT_TYPE_BITMAP; if (mt76_is_mmio(dev)) req.wow_ctrl_tlv.wakeup_hif = WOW_PCIE; else if (mt76_is_usb(dev)) req.wow_ctrl_tlv.wakeup_hif = WOW_USB; else if (mt76_is_sdio(dev)) req.wow_ctrl_tlv.wakeup_hif = WOW_GPIO; return mt76_mcu_send_msg(dev, MCU_UNI_CMD(SUSPEND), &req, sizeof(req), true); } static int mt7925_mcu_set_wow_pattern(struct mt76_dev *dev, struct ieee80211_vif *vif, u8 index, bool enable, struct cfg80211_pkt_pattern *pattern) { struct mt76_vif_link *mvif = (struct mt76_vif_link *)vif->drv_priv; struct mt7925_wow_pattern_tlv *tlv; struct sk_buff *skb; struct { u8 bss_idx; u8 pad[3]; } __packed hdr = { .bss_idx = mvif->idx, }; skb = mt76_mcu_msg_alloc(dev, NULL, sizeof(hdr) + sizeof(*tlv)); if (!skb) return -ENOMEM; skb_put_data(skb, &hdr, sizeof(hdr)); tlv = (struct mt7925_wow_pattern_tlv *)skb_put(skb, sizeof(*tlv)); tlv->tag = cpu_to_le16(UNI_SUSPEND_WOW_PATTERN); tlv->len = cpu_to_le16(sizeof(*tlv)); tlv->bss_idx = 0xF; tlv->data_len = pattern->pattern_len; tlv->enable = enable; tlv->index = index; tlv->offset = 0; memcpy(tlv->pattern, pattern->pattern, pattern->pattern_len); memcpy(tlv->mask, pattern->mask, DIV_ROUND_UP(pattern->pattern_len, 8)); return mt76_mcu_skb_send_msg(dev, skb, MCU_UNI_CMD(SUSPEND), true); } void mt7925_mcu_set_suspend_iter(void *priv, u8 *mac, struct ieee80211_vif *vif) { struct mt76_phy *phy = priv; bool suspend = !test_bit(MT76_STATE_RUNNING, &phy->state); struct ieee80211_hw *hw = phy->hw; struct cfg80211_wowlan *wowlan = hw->wiphy->wowlan_config; int i; mt76_connac_mcu_set_gtk_rekey(phy->dev, vif, suspend); mt76_connac_mcu_set_suspend_mode(phy->dev, vif, suspend, 1, true); for (i = 0; i < wowlan->n_patterns; i++) mt7925_mcu_set_wow_pattern(phy->dev, vif, i, suspend, &wowlan->patterns[i]); mt7925_connac_mcu_set_wow_ctrl(phy, vif, suspend, wowlan); } #endif /* CONFIG_PM */ static void mt7925_mcu_connection_loss_iter(void *priv, u8 *mac, struct ieee80211_vif *vif) { struct mt76_vif_link *mvif = (struct mt76_vif_link *)vif->drv_priv; struct mt7925_uni_beacon_loss_event *event = priv; if (mvif->idx != event->hdr.bss_idx) return; if (!(vif->driver_flags & IEEE80211_VIF_BEACON_FILTER) || vif->type != NL80211_IFTYPE_STATION) return; ieee80211_connection_loss(vif); } static void mt7925_mcu_connection_loss_event(struct mt792x_dev *dev, struct sk_buff *skb) { struct mt7925_uni_beacon_loss_event *event; struct mt76_phy *mphy = &dev->mt76.phy; skb_pull(skb, sizeof(struct mt7925_mcu_rxd)); event = (struct mt7925_uni_beacon_loss_event *)skb->data; ieee80211_iterate_active_interfaces_atomic(mphy->hw, IEEE80211_IFACE_ITER_RESUME_ALL, mt7925_mcu_connection_loss_iter, event); } static void mt7925_mcu_roc_iter(void *priv, u8 *mac, struct ieee80211_vif *vif) { struct mt76_vif_link *mvif = (struct mt76_vif_link *)vif->drv_priv; struct mt7925_roc_grant_tlv *grant = priv; if (ieee80211_vif_is_mld(vif) && vif->type == NL80211_IFTYPE_STATION) return; if (mvif->idx != grant->bss_idx) return; mvif->band_idx = grant->dbdcband; } static void mt7925_mcu_roc_handle_grant(struct mt792x_dev *dev, struct tlv *tlv) { struct ieee80211_hw *hw = dev->mt76.hw; struct mt7925_roc_grant_tlv *grant; int duration; grant = (struct mt7925_roc_grant_tlv *)tlv; /* should never happen */ WARN_ON_ONCE((le16_to_cpu(grant->tag) != UNI_EVENT_ROC_GRANT)); if (grant->reqtype == MT7925_ROC_REQ_ROC) ieee80211_ready_on_channel(hw); else if (grant->reqtype == MT7925_ROC_REQ_JOIN) ieee80211_iterate_active_interfaces_atomic(hw, IEEE80211_IFACE_ITER_RESUME_ALL, mt7925_mcu_roc_iter, grant); dev->phy.roc_grant = true; wake_up(&dev->phy.roc_wait); duration = le32_to_cpu(grant->max_interval); mod_timer(&dev->phy.roc_timer, jiffies + msecs_to_jiffies(duration)); } static void mt7925_mcu_handle_hif_ctrl_basic(struct mt792x_dev *dev, struct tlv *tlv) { struct mt7925_mcu_hif_ctrl_basic_tlv *basic; basic = (struct mt7925_mcu_hif_ctrl_basic_tlv *)tlv; if (basic->hifsuspend) { dev->hif_idle = true; if (!(basic->hif_tx_traffic_status == HIF_TRAFFIC_IDLE && basic->hif_rx_traffic_status == HIF_TRAFFIC_IDLE)) dev_info(dev->mt76.dev, "Hif traffic not idle.\n"); } else { dev->hif_resumed = true; } wake_up(&dev->wait); } static void mt7925_mcu_uni_hif_ctrl_event(struct mt792x_dev *dev, struct sk_buff *skb) { struct tlv *tlv; u32 tlv_len; skb_pull(skb, sizeof(struct mt7925_mcu_rxd) + 4); tlv = (struct tlv *)skb->data; tlv_len = skb->len; while (tlv_len > 0 && le16_to_cpu(tlv->len) <= tlv_len) { switch (le16_to_cpu(tlv->tag)) { case UNI_EVENT_HIF_CTRL_BASIC: mt7925_mcu_handle_hif_ctrl_basic(dev, tlv); break; default: break; } tlv_len -= le16_to_cpu(tlv->len); tlv = (struct tlv *)((char *)(tlv) + le16_to_cpu(tlv->len)); } } static void mt7925_mcu_uni_roc_event(struct mt792x_dev *dev, struct sk_buff *skb) { struct tlv *tlv; int i = 0; skb_pull(skb, sizeof(struct mt7925_mcu_rxd) + 4); while (i < skb->len) { tlv = (struct tlv *)(skb->data + i); switch (le16_to_cpu(tlv->tag)) { case UNI_EVENT_ROC_GRANT: mt7925_mcu_roc_handle_grant(dev, tlv); break; case UNI_EVENT_ROC_GRANT_SUB_LINK: break; } i += le16_to_cpu(tlv->len); } } static void mt7925_mcu_scan_event(struct mt792x_dev *dev, struct sk_buff *skb) { struct mt76_phy *mphy = &dev->mt76.phy; struct mt792x_phy *phy = mphy->priv; spin_lock_bh(&dev->mt76.lock); __skb_queue_tail(&phy->scan_event_list, skb); spin_unlock_bh(&dev->mt76.lock); ieee80211_queue_delayed_work(mphy->hw, &phy->scan_work, MT792x_HW_SCAN_TIMEOUT); } static void mt7925_mcu_tx_done_event(struct mt792x_dev *dev, struct sk_buff *skb) { #define UNI_EVENT_TX_DONE_MSG 0 #define UNI_EVENT_TX_DONE_RAW 1 struct mt7925_mcu_txs_event { u8 ver; u8 rsv[3]; u8 data[]; } __packed * txs; struct tlv *tlv; u32 tlv_len; skb_pull(skb, sizeof(struct mt7925_mcu_rxd) + 4); tlv = (struct tlv *)skb->data; tlv_len = skb->len; while (tlv_len > 0 && le16_to_cpu(tlv->len) <= tlv_len) { switch (le16_to_cpu(tlv->tag)) { case UNI_EVENT_TX_DONE_RAW: txs = (struct mt7925_mcu_txs_event *)tlv->data; mt7925_mac_add_txs(dev, txs->data); break; default: break; } tlv_len -= le16_to_cpu(tlv->len); tlv = (struct tlv *)((char *)(tlv) + le16_to_cpu(tlv->len)); } } static void mt7925_mcu_uni_debug_msg_event(struct mt792x_dev *dev, struct sk_buff *skb) { struct mt7925_uni_debug_msg { __le16 tag; __le16 len; u8 fmt; u8 rsv[3]; u8 id; u8 type:3; u8 nr_args:5; union { struct idxlog { __le16 rsv; __le32 ts; __le32 idx; u8 data[]; } __packed idx; struct txtlog { u8 len; u8 rsv; __le32 ts; u8 data[]; } __packed txt; }; } __packed * hdr; skb_pull(skb, sizeof(struct mt7925_mcu_rxd) + 4); hdr = (struct mt7925_uni_debug_msg *)skb->data; if (hdr->id == 0x28) { skb_pull(skb, offsetof(struct mt7925_uni_debug_msg, id)); wiphy_info(mt76_hw(dev)->wiphy, "%.*s", skb->len, skb->data); return; } else if (hdr->id != 0xa8) { return; } if (hdr->type == 0) { /* idx log */ int i, ret, len = PAGE_SIZE - 1, nr_val; struct page *page = dev_alloc_pages(get_order(len)); __le32 *val; char *buf, *cur; if (!page) return; buf = page_address(page); cur = buf; nr_val = (le16_to_cpu(hdr->len) - sizeof(*hdr)) / 4; val = (__le32 *)hdr->idx.data; for (i = 0; i < nr_val && len > 0; i++) { ret = snprintf(cur, len, "0x%x,", le32_to_cpu(val[i])); if (ret <= 0) break; cur += ret; len -= ret; } if (cur > buf) wiphy_info(mt76_hw(dev)->wiphy, "idx: 0x%X,%d,%s", le32_to_cpu(hdr->idx.idx), nr_val, buf); put_page(page); } else if (hdr->type == 2) { /* str log */ wiphy_info(mt76_hw(dev)->wiphy, "%.*s", hdr->txt.len, hdr->txt.data); } } static void mt7925_mcu_uni_rx_unsolicited_event(struct mt792x_dev *dev, struct sk_buff *skb) { struct mt7925_mcu_rxd *rxd; rxd = (struct mt7925_mcu_rxd *)skb->data; switch (rxd->eid) { case MCU_UNI_EVENT_HIF_CTRL: mt7925_mcu_uni_hif_ctrl_event(dev, skb); break; case MCU_UNI_EVENT_FW_LOG_2_HOST: mt7925_mcu_uni_debug_msg_event(dev, skb); break; case MCU_UNI_EVENT_ROC: mt7925_mcu_uni_roc_event(dev, skb); break; case MCU_UNI_EVENT_SCAN_DONE: mt7925_mcu_scan_event(dev, skb); return; case MCU_UNI_EVENT_TX_DONE: mt7925_mcu_tx_done_event(dev, skb); break; case MCU_UNI_EVENT_BSS_BEACON_LOSS: mt7925_mcu_connection_loss_event(dev, skb); break; case MCU_UNI_EVENT_COREDUMP: dev->fw_assert = true; mt76_connac_mcu_coredump_event(&dev->mt76, skb, &dev->coredump); return; default: break; } dev_kfree_skb(skb); } void mt7925_mcu_rx_event(struct mt792x_dev *dev, struct sk_buff *skb) { struct mt7925_mcu_rxd *rxd = (struct mt7925_mcu_rxd *)skb->data; if (skb_linearize(skb)) return; if (rxd->option & MCU_UNI_CMD_UNSOLICITED_EVENT) { mt7925_mcu_uni_rx_unsolicited_event(dev, skb); return; } mt76_mcu_rx_event(&dev->mt76, skb); } static int mt7925_mcu_sta_ba(struct mt76_dev *dev, struct mt76_vif_link *mvif, struct ieee80211_ampdu_params *params, bool enable, bool tx) { struct mt76_wcid *wcid = (struct mt76_wcid *)params->sta->drv_priv; struct sta_rec_ba_uni *ba; struct sk_buff *skb; struct tlv *tlv; int len; len = sizeof(struct sta_req_hdr) + sizeof(*ba); skb = __mt76_connac_mcu_alloc_sta_req(dev, mvif, wcid, len); if (IS_ERR(skb)) return PTR_ERR(skb); tlv = mt76_connac_mcu_add_tlv(skb, STA_REC_BA, sizeof(*ba)); ba = (struct sta_rec_ba_uni *)tlv; ba->ba_type = tx ? MT_BA_TYPE_ORIGINATOR : MT_BA_TYPE_RECIPIENT; ba->winsize = cpu_to_le16(params->buf_size); ba->ssn = cpu_to_le16(params->ssn); ba->ba_en = enable << params->tid; ba->amsdu = params->amsdu; ba->tid = params->tid; return mt76_mcu_skb_send_msg(dev, skb, MCU_UNI_CMD(STA_REC_UPDATE), true); } /** starec & wtbl **/ int mt7925_mcu_uni_tx_ba(struct mt792x_dev *dev, struct ieee80211_ampdu_params *params, bool enable) { struct mt792x_sta *msta = (struct mt792x_sta *)params->sta->drv_priv; struct mt792x_vif *mvif = msta->vif; if (enable && !params->amsdu) msta->deflink.wcid.amsdu = false; return mt7925_mcu_sta_ba(&dev->mt76, &mvif->bss_conf.mt76, params, enable, true); } int mt7925_mcu_uni_rx_ba(struct mt792x_dev *dev, struct ieee80211_ampdu_params *params, bool enable) { struct mt792x_sta *msta = (struct mt792x_sta *)params->sta->drv_priv; struct mt792x_vif *mvif = msta->vif; return mt7925_mcu_sta_ba(&dev->mt76, &mvif->bss_conf.mt76, params, enable, false); } static int mt7925_mcu_read_eeprom(struct mt792x_dev *dev, u32 offset, u8 *val) { struct { u8 rsv[4]; __le16 tag; __le16 len; __le32 addr; __le32 valid; u8 data[MT7925_EEPROM_BLOCK_SIZE]; } __packed req = { .tag = cpu_to_le16(1), .len = cpu_to_le16(sizeof(req) - 4), .addr = cpu_to_le32(round_down(offset, MT7925_EEPROM_BLOCK_SIZE)), }; struct evt { u8 rsv[4]; __le16 tag; __le16 len; __le32 ver; __le32 addr; __le32 valid; __le32 size; __le32 magic_num; __le32 type; __le32 rsv1[4]; u8 data[32]; } __packed *res; struct sk_buff *skb; int ret; ret = mt76_mcu_send_and_get_msg(&dev->mt76, MCU_WM_UNI_CMD_QUERY(EFUSE_CTRL), &req, sizeof(req), true, &skb); if (ret) return ret; res = (struct evt *)skb->data; *val = res->data[offset % MT7925_EEPROM_BLOCK_SIZE]; dev_kfree_skb(skb); return 0; } static int mt7925_load_clc(struct mt792x_dev *dev, const char *fw_name) { const struct mt76_connac2_fw_trailer *hdr; const struct mt76_connac2_fw_region *region; const struct mt7925_clc *clc; struct mt76_dev *mdev = &dev->mt76; struct mt792x_phy *phy = &dev->phy; const struct firmware *fw; #if defined(__linux__) u8 *clc_base = NULL, hw_encap = 0; #elif defined(__FreeBSD__) const u8 *clc_base = NULL; u8 hw_encap = 0; #endif int ret, i, len, offset = 0; dev->phy.clc_chan_conf = 0xff; if (mt7925_disable_clc || mt76_is_usb(&dev->mt76)) return 0; if (mt76_is_mmio(&dev->mt76)) { ret = mt7925_mcu_read_eeprom(dev, MT_EE_HW_TYPE, &hw_encap); if (ret) return ret; hw_encap = u8_get_bits(hw_encap, MT_EE_HW_TYPE_ENCAP); } ret = request_firmware(&fw, fw_name, mdev->dev); if (ret) return ret; if (!fw || !fw->data || fw->size < sizeof(*hdr)) { dev_err(mdev->dev, "Invalid firmware\n"); ret = -EINVAL; goto out; } hdr = (const void *)(fw->data + fw->size - sizeof(*hdr)); for (i = 0; i < hdr->n_region; i++) { region = (const void *)((const u8 *)hdr - (hdr->n_region - i) * sizeof(*region)); len = le32_to_cpu(region->len); /* check if we have valid buffer size */ if (offset + len > fw->size) { dev_err(mdev->dev, "Invalid firmware region\n"); ret = -EINVAL; goto out; } if ((region->feature_set & FW_FEATURE_NON_DL) && region->type == FW_TYPE_CLC) { #if defined(__linux__) clc_base = (u8 *)(fw->data + offset); #elif defined(__FreeBSD__) clc_base = (const u8 *)(fw->data + offset); #endif break; } offset += len; } if (!clc_base) goto out; for (offset = 0; offset < len; offset += le32_to_cpu(clc->len)) { clc = (const struct mt7925_clc *)(clc_base + offset); if (clc->idx >= ARRAY_SIZE(phy->clc)) break; /* do not init buf again if chip reset triggered */ if (phy->clc[clc->idx]) continue; /* header content sanity */ if ((clc->idx == MT792x_CLC_BE_CTRL && u8_get_bits(clc->t2.type, MT_EE_HW_TYPE_ENCAP) != hw_encap) || u8_get_bits(clc->t0.type, MT_EE_HW_TYPE_ENCAP) != hw_encap) continue; phy->clc[clc->idx] = devm_kmemdup(mdev->dev, clc, le32_to_cpu(clc->len), GFP_KERNEL); if (!phy->clc[clc->idx]) { ret = -ENOMEM; goto out; } } ret = mt7925_mcu_set_clc(dev, "00", ENVIRON_INDOOR); out: release_firmware(fw); return ret; } int mt7925_mcu_fw_log_2_host(struct mt792x_dev *dev, u8 ctrl) { struct { u8 _rsv[4]; __le16 tag; __le16 len; u8 ctrl; u8 interval; u8 _rsv2[2]; } __packed req = { .tag = cpu_to_le16(UNI_WSYS_CONFIG_FW_LOG_CTRL), .len = cpu_to_le16(sizeof(req) - 4), .ctrl = ctrl, }; int ret; ret = mt76_mcu_send_and_get_msg(&dev->mt76, MCU_UNI_CMD(WSYS_CONFIG), &req, sizeof(req), true, NULL); return ret; } int mt7925_mcu_get_temperature(struct mt792x_phy *phy) { struct { u8 _rsv[4]; __le16 tag; __le16 len; u8 _rsv2[4]; } __packed req = { .tag = cpu_to_le16(0x0), .len = cpu_to_le16(sizeof(req) - 4), }; struct mt7925_thermal_evt { u8 rsv[4]; __le32 temperature; } __packed * evt; struct mt792x_dev *dev = phy->dev; int temperature, ret; struct sk_buff *skb; ret = mt76_mcu_send_and_get_msg(&dev->mt76, MCU_WM_UNI_CMD_QUERY(THERMAL), &req, sizeof(req), true, &skb); if (ret) return ret; skb_pull(skb, 4 + sizeof(struct tlv)); evt = (struct mt7925_thermal_evt *)skb->data; temperature = le32_to_cpu(evt->temperature); dev_kfree_skb(skb); return temperature; } static void mt7925_mcu_parse_phy_cap(struct mt792x_dev *dev, char *data) { struct mt76_phy *mphy = &dev->mt76.phy; struct mt76_dev *mdev = mphy->dev; struct mt7925_mcu_phy_cap { u8 ht; u8 vht; u8 _5g; u8 max_bw; u8 nss; u8 dbdc; u8 tx_ldpc; u8 rx_ldpc; u8 tx_stbc; u8 rx_stbc; u8 hw_path; u8 he; u8 eht; } __packed * cap; enum { WF0_24G, WF0_5G }; cap = (struct mt7925_mcu_phy_cap *)data; mdev->phy.antenna_mask = BIT(cap->nss) - 1; mdev->phy.chainmask = mdev->phy.antenna_mask; mdev->phy.cap.has_2ghz = cap->hw_path & BIT(WF0_24G); mdev->phy.cap.has_5ghz = cap->hw_path & BIT(WF0_5G); } static void mt7925_mcu_parse_eml_cap(struct mt792x_dev *dev, char *data) { struct mt7925_mcu_eml_cap { u8 rsv[4]; __le16 eml_cap; u8 rsv2[6]; } __packed * cap; cap = (struct mt7925_mcu_eml_cap *)data; dev->phy.eml_cap = le16_to_cpu(cap->eml_cap); } static int mt7925_mcu_get_nic_capability(struct mt792x_dev *dev) { struct mt76_phy *mphy = &dev->mt76.phy; struct { u8 _rsv[4]; __le16 tag; __le16 len; } __packed req = { .tag = cpu_to_le16(UNI_CHIP_CONFIG_NIC_CAPA), .len = cpu_to_le16(sizeof(req) - 4), }; struct mt76_connac_cap_hdr { __le16 n_element; u8 rsv[2]; } __packed * hdr; struct sk_buff *skb; int ret, i; ret = mt76_mcu_send_and_get_msg(&dev->mt76, MCU_UNI_CMD(CHIP_CONFIG), &req, sizeof(req), true, &skb); if (ret) return ret; hdr = (struct mt76_connac_cap_hdr *)skb->data; if (skb->len < sizeof(*hdr)) { ret = -EINVAL; goto out; } skb_pull(skb, sizeof(*hdr)); for (i = 0; i < le16_to_cpu(hdr->n_element); i++) { struct tlv *tlv = (struct tlv *)skb->data; int len; if (skb->len < sizeof(*tlv)) break; len = le16_to_cpu(tlv->len); if (skb->len < len) break; switch (le16_to_cpu(tlv->tag)) { case MT_NIC_CAP_6G: mphy->cap.has_6ghz = !!tlv->data[0]; break; case MT_NIC_CAP_MAC_ADDR: memcpy(mphy->macaddr, (void *)tlv->data, ETH_ALEN); break; case MT_NIC_CAP_PHY: mt7925_mcu_parse_phy_cap(dev, tlv->data); break; case MT_NIC_CAP_CHIP_CAP: dev->phy.chip_cap = le64_to_cpu(*(__le64 *)tlv->data); break; case MT_NIC_CAP_EML_CAP: mt7925_mcu_parse_eml_cap(dev, tlv->data); break; default: break; } skb_pull(skb, len); } out: dev_kfree_skb(skb); return ret; } int mt7925_mcu_chip_config(struct mt792x_dev *dev, const char *cmd) { u16 len = strlen(cmd) + 1; struct { u8 _rsv[4]; __le16 tag; __le16 len; struct mt76_connac_config config; } __packed req = { .tag = cpu_to_le16(UNI_CHIP_CONFIG_CHIP_CFG), .len = cpu_to_le16(sizeof(req) - 4), .config = { .resp_type = 0, .type = 0, .data_size = cpu_to_le16(len), }, }; memcpy(req.config.data, cmd, len); return mt76_mcu_send_msg(&dev->mt76, MCU_UNI_CMD(CHIP_CONFIG), &req, sizeof(req), false); } int mt7925_mcu_set_deep_sleep(struct mt792x_dev *dev, bool enable) { char cmd[16]; snprintf(cmd, sizeof(cmd), "KeepFullPwr %d", !enable); return mt7925_mcu_chip_config(dev, cmd); } EXPORT_SYMBOL_GPL(mt7925_mcu_set_deep_sleep); int mt7925_mcu_set_thermal_protect(struct mt792x_dev *dev) { char cmd[64]; int ret = 0; snprintf(cmd, sizeof(cmd), "ThermalProtGband %d %d %d %d %d %d %d %d %d %d", 0, 100, 90, 80, 30, 1, 1, 115, 105, 5); ret = mt7925_mcu_chip_config(dev, cmd); snprintf(cmd, sizeof(cmd), "ThermalProtAband %d %d %d %d %d %d %d %d %d %d", 1, 100, 90, 80, 30, 1, 1, 115, 105, 5); ret |= mt7925_mcu_chip_config(dev, cmd); return ret; } EXPORT_SYMBOL_GPL(mt7925_mcu_set_thermal_protect); int mt7925_run_firmware(struct mt792x_dev *dev) { int err; err = mt792x_load_firmware(dev); if (err) return err; err = mt7925_mcu_get_nic_capability(dev); if (err) return err; set_bit(MT76_STATE_MCU_RUNNING, &dev->mphy.state); err = mt7925_load_clc(dev, mt792x_ram_name(dev)); if (err) return err; return mt7925_mcu_fw_log_2_host(dev, 1); } EXPORT_SYMBOL_GPL(mt7925_run_firmware); static void mt7925_mcu_sta_hdr_trans_tlv(struct sk_buff *skb, struct ieee80211_vif *vif, struct ieee80211_link_sta *link_sta) { struct mt792x_vif *mvif = (struct mt792x_vif *)vif->drv_priv; struct sta_rec_hdr_trans *hdr_trans; struct mt76_wcid *wcid; struct tlv *tlv; tlv = mt76_connac_mcu_add_tlv(skb, STA_REC_HDR_TRANS, sizeof(*hdr_trans)); hdr_trans = (struct sta_rec_hdr_trans *)tlv; hdr_trans->dis_rx_hdr_tran = true; if (vif->type == NL80211_IFTYPE_STATION) hdr_trans->to_ds = true; else hdr_trans->from_ds = true; if (link_sta) { struct mt792x_sta *msta = (struct mt792x_sta *)link_sta->sta->drv_priv; struct mt792x_link_sta *mlink; mlink = mt792x_sta_to_link(msta, link_sta->link_id); wcid = &mlink->wcid; } else { wcid = &mvif->sta.deflink.wcid; } if (!wcid) return; hdr_trans->dis_rx_hdr_tran = !test_bit(MT_WCID_FLAG_HDR_TRANS, &wcid->flags); if (test_bit(MT_WCID_FLAG_4ADDR, &wcid->flags)) { hdr_trans->to_ds = true; hdr_trans->from_ds = true; } } int mt7925_mcu_wtbl_update_hdr_trans(struct mt792x_dev *dev, struct ieee80211_vif *vif, struct ieee80211_sta *sta, int link_id) { struct mt792x_vif *mvif = (struct mt792x_vif *)vif->drv_priv; struct ieee80211_link_sta *link_sta = sta ? &sta->deflink : NULL; struct mt792x_link_sta *mlink; struct mt792x_bss_conf *mconf; struct mt792x_sta *msta; struct sk_buff *skb; msta = sta ? (struct mt792x_sta *)sta->drv_priv : &mvif->sta; mlink = mt792x_sta_to_link(msta, link_id); link_sta = mt792x_sta_to_link_sta(vif, sta, link_id); mconf = mt792x_vif_to_link(mvif, link_id); skb = __mt76_connac_mcu_alloc_sta_req(&dev->mt76, &mconf->mt76, &mlink->wcid, MT7925_STA_UPDATE_MAX_SIZE); if (IS_ERR(skb)) return PTR_ERR(skb); /* starec hdr trans */ mt7925_mcu_sta_hdr_trans_tlv(skb, vif, link_sta); return mt76_mcu_skb_send_msg(&dev->mt76, skb, MCU_WMWA_UNI_CMD(STA_REC_UPDATE), true); } int mt7925_mcu_set_tx(struct mt792x_dev *dev, struct ieee80211_bss_conf *bss_conf) { #define MCU_EDCA_AC_PARAM 0 #define WMM_AIFS_SET BIT(0) #define WMM_CW_MIN_SET BIT(1) #define WMM_CW_MAX_SET BIT(2) #define WMM_TXOP_SET BIT(3) #define WMM_PARAM_SET (WMM_AIFS_SET | WMM_CW_MIN_SET | \ WMM_CW_MAX_SET | WMM_TXOP_SET) struct mt792x_bss_conf *mconf = mt792x_link_conf_to_mconf(bss_conf); struct { u8 bss_idx; u8 __rsv[3]; } __packed hdr = { .bss_idx = mconf->mt76.idx, }; struct sk_buff *skb; int len = sizeof(hdr) + IEEE80211_NUM_ACS * sizeof(struct edca); int ac; skb = mt76_mcu_msg_alloc(&dev->mt76, NULL, len); if (!skb) return -ENOMEM; skb_put_data(skb, &hdr, sizeof(hdr)); for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) { struct ieee80211_tx_queue_params *q = &mconf->queue_params[ac]; struct edca *e; struct tlv *tlv; tlv = mt76_connac_mcu_add_tlv(skb, MCU_EDCA_AC_PARAM, sizeof(*e)); e = (struct edca *)tlv; e->set = WMM_PARAM_SET; e->queue = ac; e->aifs = q->aifs; e->txop = cpu_to_le16(q->txop); if (q->cw_min) e->cw_min = fls(q->cw_min); else e->cw_min = 5; if (q->cw_max) e->cw_max = fls(q->cw_max); else e->cw_max = 10; } return mt76_mcu_skb_send_msg(&dev->mt76, skb, MCU_UNI_CMD(EDCA_UPDATE), true); } static int mt7925_mcu_sta_key_tlv(struct mt76_wcid *wcid, struct mt76_connac_sta_key_conf *sta_key_conf, struct sk_buff *skb, struct ieee80211_key_conf *key, enum set_key_cmd cmd, struct mt792x_sta *msta) { struct mt792x_vif *mvif = msta->vif; struct mt792x_bss_conf *mconf = mt792x_vif_to_link(mvif, wcid->link_id); struct sta_rec_sec_uni *sec; struct ieee80211_sta *sta; struct ieee80211_vif *vif; struct tlv *tlv; sta = msta == &mvif->sta ? NULL : container_of((void *)msta, struct ieee80211_sta, drv_priv); vif = container_of((void *)mvif, struct ieee80211_vif, drv_priv); tlv = mt76_connac_mcu_add_tlv(skb, STA_REC_KEY_V3, sizeof(*sec)); sec = (struct sta_rec_sec_uni *)tlv; sec->bss_idx = mconf->mt76.idx; sec->is_authenticator = 0; sec->mgmt_prot = 1; /* only used in MLO mode */ sec->wlan_idx = (u8)wcid->idx; if (sta) { struct ieee80211_link_sta *link_sta; sec->tx_key = 1; sec->key_type = 1; link_sta = mt792x_sta_to_link_sta(vif, sta, wcid->link_id); if (link_sta) memcpy(sec->peer_addr, link_sta->addr, ETH_ALEN); } else { struct ieee80211_bss_conf *link_conf; link_conf = mt792x_vif_to_bss_conf(vif, wcid->link_id); if (link_conf) memcpy(sec->peer_addr, link_conf->bssid, ETH_ALEN); } if (cmd == SET_KEY) { u8 cipher; sec->add = 1; cipher = mt7925_mcu_get_cipher(key->cipher); if (cipher == CONNAC3_CIPHER_NONE) return -EOPNOTSUPP; if (cipher == CONNAC3_CIPHER_BIP_CMAC_128) { sec->cipher_id = CONNAC3_CIPHER_BIP_CMAC_128; sec->key_id = sta_key_conf->keyidx; sec->key_len = 32; memcpy(sec->key, sta_key_conf->key, 16); memcpy(sec->key + 16, key->key, 16); } else { sec->cipher_id = cipher; sec->key_id = key->keyidx; sec->key_len = key->keylen; memcpy(sec->key, key->key, key->keylen); if (cipher == CONNAC3_CIPHER_TKIP) { /* Rx/Tx MIC keys are swapped */ memcpy(sec->key + 16, key->key + 24, 8); memcpy(sec->key + 24, key->key + 16, 8); } /* store key_conf for BIP batch update */ if (cipher == CONNAC3_CIPHER_AES_CCMP) { memcpy(sta_key_conf->key, key->key, key->keylen); sta_key_conf->keyidx = key->keyidx; } } } else { sec->add = 0; } return 0; } int mt7925_mcu_add_key(struct mt76_dev *dev, struct ieee80211_vif *vif, struct mt76_connac_sta_key_conf *sta_key_conf, struct ieee80211_key_conf *key, int mcu_cmd, struct mt76_wcid *wcid, enum set_key_cmd cmd, struct mt792x_sta *msta) { struct mt792x_vif *mvif = (struct mt792x_vif *)vif->drv_priv; struct mt792x_bss_conf *mconf = mt792x_vif_to_link(mvif, wcid->link_id); struct sk_buff *skb; int ret; skb = __mt76_connac_mcu_alloc_sta_req(dev, &mconf->mt76, wcid, MT7925_STA_UPDATE_MAX_SIZE); if (IS_ERR(skb)) return PTR_ERR(skb); ret = mt7925_mcu_sta_key_tlv(wcid, sta_key_conf, skb, key, cmd, msta); if (ret) return ret; return mt76_mcu_skb_send_msg(dev, skb, mcu_cmd, true); } int mt7925_mcu_set_mlo_roc(struct mt792x_bss_conf *mconf, u16 sel_links, int duration, u8 token_id) { struct mt792x_vif *mvif = mconf->vif; struct ieee80211_vif *vif = container_of((void *)mvif, struct ieee80211_vif, drv_priv); struct ieee80211_bss_conf *link_conf; struct ieee80211_channel *chan; const u8 ch_band[] = { [NL80211_BAND_2GHZ] = 1, [NL80211_BAND_5GHZ] = 2, [NL80211_BAND_6GHZ] = 3, }; enum mt7925_roc_req type; int center_ch, i = 0; bool is_AG_band = false; struct { u8 id; u8 bss_idx; u16 tag; struct mt792x_bss_conf *mconf; struct ieee80211_channel *chan; } links[2]; struct { struct { u8 rsv[4]; } __packed hdr; struct roc_acquire_tlv roc[2]; } __packed req = { .roc[0].tag = cpu_to_le16(UNI_ROC_NUM), .roc[0].len = cpu_to_le16(sizeof(struct roc_acquire_tlv)), .roc[1].tag = cpu_to_le16(UNI_ROC_NUM), .roc[1].len = cpu_to_le16(sizeof(struct roc_acquire_tlv)) }; if (!mconf || hweight16(vif->valid_links) < 2 || hweight16(sel_links) != 2) return -EPERM; for (i = 0; i < ARRAY_SIZE(links); i++) { links[i].id = i ? __ffs(~BIT(mconf->link_id) & sel_links) : mconf->link_id; link_conf = mt792x_vif_to_bss_conf(vif, links[i].id); if (WARN_ON_ONCE(!link_conf)) return -EPERM; links[i].chan = link_conf->chanreq.oper.chan; if (WARN_ON_ONCE(!links[i].chan)) return -EPERM; links[i].mconf = mt792x_vif_to_link(mvif, links[i].id); links[i].tag = links[i].id == mconf->link_id ? UNI_ROC_ACQUIRE : UNI_ROC_SUB_LINK; is_AG_band |= links[i].chan->band == NL80211_BAND_2GHZ; } if (vif->cfg.eml_cap & IEEE80211_EML_CAP_EMLSR_SUPP) type = is_AG_band ? MT7925_ROC_REQ_MLSR_AG : MT7925_ROC_REQ_MLSR_AA; else type = MT7925_ROC_REQ_JOIN; for (i = 0; i < ARRAY_SIZE(links) && i < hweight16(vif->active_links); i++) { if (WARN_ON_ONCE(!links[i].mconf || !links[i].chan)) continue; chan = links[i].chan; center_ch = ieee80211_frequency_to_channel(chan->center_freq); req.roc[i].len = cpu_to_le16(sizeof(struct roc_acquire_tlv)); req.roc[i].tag = cpu_to_le16(links[i].tag); req.roc[i].tokenid = token_id; req.roc[i].reqtype = type; req.roc[i].maxinterval = cpu_to_le32(duration); req.roc[i].bss_idx = links[i].mconf->mt76.idx; req.roc[i].control_channel = chan->hw_value; req.roc[i].bw = CMD_CBW_20MHZ; req.roc[i].bw_from_ap = CMD_CBW_20MHZ; req.roc[i].center_chan = center_ch; req.roc[i].center_chan_from_ap = center_ch; req.roc[i].center_chan2 = 0; req.roc[i].center_chan2_from_ap = 0; /* STR : 0xfe indicates BAND_ALL with enabling DBDC * EMLSR : 0xff indicates (BAND_AUTO) without DBDC */ req.roc[i].dbdcband = type == MT7925_ROC_REQ_JOIN ? 0xfe : 0xff; if (chan->hw_value < center_ch) req.roc[i].sco = 1; /* SCA */ else if (chan->hw_value > center_ch) req.roc[i].sco = 3; /* SCB */ req.roc[i].band = ch_band[chan->band]; } return mt76_mcu_send_msg(&mvif->phy->dev->mt76, MCU_UNI_CMD(ROC), &req, sizeof(req), true); } int mt7925_mcu_set_roc(struct mt792x_phy *phy, struct mt792x_bss_conf *mconf, struct ieee80211_channel *chan, int duration, enum mt7925_roc_req type, u8 token_id) { int center_ch = ieee80211_frequency_to_channel(chan->center_freq); struct mt792x_dev *dev = phy->dev; struct { struct { u8 rsv[4]; } __packed hdr; struct roc_acquire_tlv roc; } __packed req = { .roc = { .tag = cpu_to_le16(UNI_ROC_ACQUIRE), .len = cpu_to_le16(sizeof(struct roc_acquire_tlv)), .tokenid = token_id, .reqtype = type, .maxinterval = cpu_to_le32(duration), .bss_idx = mconf->mt76.idx, .control_channel = chan->hw_value, .bw = CMD_CBW_20MHZ, .bw_from_ap = CMD_CBW_20MHZ, .center_chan = center_ch, .center_chan_from_ap = center_ch, .dbdcband = 0xff, /* auto */ }, }; if (chan->hw_value < center_ch) req.roc.sco = 1; /* SCA */ else if (chan->hw_value > center_ch) req.roc.sco = 3; /* SCB */ switch (chan->band) { case NL80211_BAND_6GHZ: req.roc.band = 3; break; case NL80211_BAND_5GHZ: req.roc.band = 2; break; default: req.roc.band = 1; break; } return mt76_mcu_send_msg(&dev->mt76, MCU_UNI_CMD(ROC), &req, sizeof(req), true); } int mt7925_mcu_abort_roc(struct mt792x_phy *phy, struct mt792x_bss_conf *mconf, u8 token_id) { struct mt792x_dev *dev = phy->dev; struct { struct { u8 rsv[4]; } __packed hdr; struct roc_abort_tlv { __le16 tag; __le16 len; u8 bss_idx; u8 tokenid; u8 dbdcband; u8 rsv[5]; } __packed abort; } __packed req = { .abort = { .tag = cpu_to_le16(UNI_ROC_ABORT), .len = cpu_to_le16(sizeof(struct roc_abort_tlv)), .tokenid = token_id, .bss_idx = mconf->mt76.idx, .dbdcband = 0xff, /* auto*/ }, }; return mt76_mcu_send_msg(&dev->mt76, MCU_UNI_CMD(ROC), &req, sizeof(req), true); } int mt7925_mcu_set_eeprom(struct mt792x_dev *dev) { struct { u8 _rsv[4]; __le16 tag; __le16 len; u8 buffer_mode; u8 format; __le16 buf_len; } __packed req = { .tag = cpu_to_le16(UNI_EFUSE_BUFFER_MODE), .len = cpu_to_le16(sizeof(req) - 4), .buffer_mode = EE_MODE_EFUSE, .format = EE_FORMAT_WHOLE }; return mt76_mcu_send_and_get_msg(&dev->mt76, MCU_UNI_CMD(EFUSE_CTRL), &req, sizeof(req), true, NULL); } EXPORT_SYMBOL_GPL(mt7925_mcu_set_eeprom); int mt7925_mcu_uni_bss_ps(struct mt792x_dev *dev, struct ieee80211_bss_conf *link_conf) { struct mt792x_bss_conf *mconf = mt792x_link_conf_to_mconf(link_conf); struct { struct { u8 bss_idx; u8 pad[3]; } __packed hdr; struct ps_tlv { __le16 tag; __le16 len; u8 ps_state; /* 0: device awake * 1: static power save * 2: dynamic power saving * 3: enter TWT power saving * 4: leave TWT power saving */ u8 pad[3]; } __packed ps; } __packed ps_req = { .hdr = { .bss_idx = mconf->mt76.idx, }, .ps = { .tag = cpu_to_le16(UNI_BSS_INFO_PS), .len = cpu_to_le16(sizeof(struct ps_tlv)), .ps_state = link_conf->vif->cfg.ps ? 2 : 0, }, }; if (link_conf->vif->type != NL80211_IFTYPE_STATION) return -EOPNOTSUPP; return mt76_mcu_send_msg(&dev->mt76, MCU_UNI_CMD(BSS_INFO_UPDATE), &ps_req, sizeof(ps_req), true); } int mt7925_mcu_uni_bss_bcnft(struct mt792x_dev *dev, struct ieee80211_bss_conf *link_conf, bool enable) { struct mt792x_bss_conf *mconf = mt792x_link_conf_to_mconf(link_conf); struct { struct { u8 bss_idx; u8 pad[3]; } __packed hdr; struct bcnft_tlv { __le16 tag; __le16 len; __le16 bcn_interval; u8 dtim_period; u8 bmc_delivered_ac; u8 bmc_triggered_ac; u8 pad[3]; } __packed bcnft; } __packed bcnft_req = { .hdr = { .bss_idx = mconf->mt76.idx, }, .bcnft = { .tag = cpu_to_le16(UNI_BSS_INFO_BCNFT), .len = cpu_to_le16(sizeof(struct bcnft_tlv)), .bcn_interval = cpu_to_le16(link_conf->beacon_int), .dtim_period = link_conf->dtim_period, }, }; if (link_conf->vif->type != NL80211_IFTYPE_STATION) return 0; return mt76_mcu_send_msg(&dev->mt76, MCU_UNI_CMD(BSS_INFO_UPDATE), &bcnft_req, sizeof(bcnft_req), true); } int mt7925_mcu_set_bss_pm(struct mt792x_dev *dev, struct ieee80211_bss_conf *link_conf, bool enable) { struct mt792x_bss_conf *mconf = mt792x_link_conf_to_mconf(link_conf); struct { struct { u8 bss_idx; u8 pad[3]; } __packed hdr; struct bcnft_tlv { __le16 tag; __le16 len; __le16 bcn_interval; u8 dtim_period; u8 bmc_delivered_ac; u8 bmc_triggered_ac; u8 pad[3]; } __packed enable; } req = { .hdr = { .bss_idx = mconf->mt76.idx, }, .enable = { .tag = cpu_to_le16(UNI_BSS_INFO_BCNFT), .len = cpu_to_le16(sizeof(struct bcnft_tlv)), .dtim_period = link_conf->dtim_period, .bcn_interval = cpu_to_le16(link_conf->beacon_int), }, }; struct { struct { u8 bss_idx; u8 pad[3]; } __packed hdr; struct pm_disable { __le16 tag; __le16 len; } __packed disable; } req1 = { .hdr = { .bss_idx = mconf->mt76.idx, }, .disable = { .tag = cpu_to_le16(UNI_BSS_INFO_PM_DISABLE), .len = cpu_to_le16(sizeof(struct pm_disable)) }, }; int err; err = mt76_mcu_send_msg(&dev->mt76, MCU_UNI_CMD(BSS_INFO_UPDATE), &req1, sizeof(req1), true); if (err < 0 || !enable) return err; return mt76_mcu_send_msg(&dev->mt76, MCU_UNI_CMD(BSS_INFO_UPDATE), &req, sizeof(req), true); } static void mt7925_mcu_sta_he_tlv(struct sk_buff *skb, struct ieee80211_link_sta *link_sta) { if (!link_sta->he_cap.has_he) return; mt76_connac_mcu_sta_he_tlv_v2(skb, link_sta->sta); } static void mt7925_mcu_sta_he_6g_tlv(struct sk_buff *skb, struct ieee80211_link_sta *link_sta) { struct sta_rec_he_6g_capa *he_6g; struct tlv *tlv; if (!link_sta->he_6ghz_capa.capa) return; tlv = mt76_connac_mcu_add_tlv(skb, STA_REC_HE_6G, sizeof(*he_6g)); he_6g = (struct sta_rec_he_6g_capa *)tlv; he_6g->capa = link_sta->he_6ghz_capa.capa; } static void mt7925_mcu_sta_eht_tlv(struct sk_buff *skb, struct ieee80211_link_sta *link_sta) { struct ieee80211_eht_mcs_nss_supp *mcs_map; struct ieee80211_eht_cap_elem_fixed *elem; struct sta_rec_eht *eht; struct tlv *tlv; if (!link_sta->eht_cap.has_eht) return; mcs_map = &link_sta->eht_cap.eht_mcs_nss_supp; elem = &link_sta->eht_cap.eht_cap_elem; tlv = mt76_connac_mcu_add_tlv(skb, STA_REC_EHT, sizeof(*eht)); eht = (struct sta_rec_eht *)tlv; eht->tid_bitmap = 0xff; eht->mac_cap = cpu_to_le16(*(u16 *)elem->mac_cap_info); eht->phy_cap = cpu_to_le64(*(u64 *)elem->phy_cap_info); eht->phy_cap_ext = cpu_to_le64(elem->phy_cap_info[8]); if (link_sta->bandwidth == IEEE80211_STA_RX_BW_20) memcpy(eht->mcs_map_bw20, &mcs_map->only_20mhz, sizeof(eht->mcs_map_bw20)); memcpy(eht->mcs_map_bw80, &mcs_map->bw._80, sizeof(eht->mcs_map_bw80)); memcpy(eht->mcs_map_bw160, &mcs_map->bw._160, sizeof(eht->mcs_map_bw160)); } static void mt7925_mcu_sta_ht_tlv(struct sk_buff *skb, struct ieee80211_link_sta *link_sta) { struct sta_rec_ht *ht; struct tlv *tlv; if (!link_sta->ht_cap.ht_supported) return; tlv = mt76_connac_mcu_add_tlv(skb, STA_REC_HT, sizeof(*ht)); ht = (struct sta_rec_ht *)tlv; ht->ht_cap = cpu_to_le16(link_sta->ht_cap.cap); } static void mt7925_mcu_sta_vht_tlv(struct sk_buff *skb, struct ieee80211_link_sta *link_sta) { struct sta_rec_vht *vht; struct tlv *tlv; /* For 6G band, this tlv is necessary to let hw work normally */ if (!link_sta->he_6ghz_capa.capa && !link_sta->vht_cap.vht_supported) return; tlv = mt76_connac_mcu_add_tlv(skb, STA_REC_VHT, sizeof(*vht)); vht = (struct sta_rec_vht *)tlv; vht->vht_cap = cpu_to_le32(link_sta->vht_cap.cap); vht->vht_rx_mcs_map = link_sta->vht_cap.vht_mcs.rx_mcs_map; vht->vht_tx_mcs_map = link_sta->vht_cap.vht_mcs.tx_mcs_map; } static void mt7925_mcu_sta_amsdu_tlv(struct sk_buff *skb, struct ieee80211_vif *vif, struct ieee80211_link_sta *link_sta) { struct mt792x_sta *msta = (struct mt792x_sta *)link_sta->sta->drv_priv; struct mt792x_link_sta *mlink; struct sta_rec_amsdu *amsdu; struct tlv *tlv; if (vif->type != NL80211_IFTYPE_STATION && vif->type != NL80211_IFTYPE_AP) return; if (!link_sta->agg.max_amsdu_len) return; tlv = mt76_connac_mcu_add_tlv(skb, STA_REC_HW_AMSDU, sizeof(*amsdu)); amsdu = (struct sta_rec_amsdu *)tlv; amsdu->max_amsdu_num = 8; amsdu->amsdu_en = true; mlink = mt792x_sta_to_link(msta, link_sta->link_id); mlink->wcid.amsdu = true; switch (link_sta->agg.max_amsdu_len) { case IEEE80211_MAX_MPDU_LEN_VHT_11454: amsdu->max_mpdu_size = IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_11454; return; case IEEE80211_MAX_MPDU_LEN_HT_7935: case IEEE80211_MAX_MPDU_LEN_VHT_7991: amsdu->max_mpdu_size = IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_7991; return; default: amsdu->max_mpdu_size = IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_3895; return; } } static void mt7925_mcu_sta_phy_tlv(struct sk_buff *skb, struct ieee80211_vif *vif, struct ieee80211_link_sta *link_sta) { struct mt792x_vif *mvif = (struct mt792x_vif *)vif->drv_priv; struct ieee80211_bss_conf *link_conf; struct cfg80211_chan_def *chandef; struct mt792x_bss_conf *mconf; struct sta_rec_phy *phy; struct tlv *tlv; u8 af = 0, mm = 0; link_conf = mt792x_vif_to_bss_conf(vif, link_sta->link_id); mconf = mt792x_vif_to_link(mvif, link_sta->link_id); chandef = mconf->mt76.ctx ? &mconf->mt76.ctx->def : &link_conf->chanreq.oper; tlv = mt76_connac_mcu_add_tlv(skb, STA_REC_PHY, sizeof(*phy)); phy = (struct sta_rec_phy *)tlv; phy->phy_type = mt76_connac_get_phy_mode_v2(mvif->phy->mt76, vif, chandef->chan->band, link_sta); phy->basic_rate = cpu_to_le16((u16)link_conf->basic_rates); if (link_sta->ht_cap.ht_supported) { af = link_sta->ht_cap.ampdu_factor; mm = link_sta->ht_cap.ampdu_density; } if (link_sta->vht_cap.vht_supported) { u8 vht_af = FIELD_GET(IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_MASK, link_sta->vht_cap.cap); af = max_t(u8, af, vht_af); } if (link_sta->he_6ghz_capa.capa) { af = le16_get_bits(link_sta->he_6ghz_capa.capa, IEEE80211_HE_6GHZ_CAP_MAX_AMPDU_LEN_EXP); mm = le16_get_bits(link_sta->he_6ghz_capa.capa, IEEE80211_HE_6GHZ_CAP_MIN_MPDU_START); } phy->ampdu = FIELD_PREP(IEEE80211_HT_AMPDU_PARM_FACTOR, af) | FIELD_PREP(IEEE80211_HT_AMPDU_PARM_DENSITY, mm); phy->max_ampdu_len = af; } static void mt7925_mcu_sta_state_v2_tlv(struct mt76_phy *mphy, struct sk_buff *skb, struct ieee80211_link_sta *link_sta, struct ieee80211_vif *vif, u8 rcpi, u8 sta_state) { struct sta_rec_state_v2 { __le16 tag; __le16 len; u8 state; u8 rsv[3]; __le32 flags; u8 vht_opmode; u8 action; u8 rsv2[2]; } __packed * state; struct tlv *tlv; tlv = mt76_connac_mcu_add_tlv(skb, STA_REC_STATE, sizeof(*state)); state = (struct sta_rec_state_v2 *)tlv; state->state = sta_state; if (link_sta->vht_cap.vht_supported) { state->vht_opmode = link_sta->bandwidth; state->vht_opmode |= link_sta->rx_nss << IEEE80211_OPMODE_NOTIF_RX_NSS_SHIFT; } } static void mt7925_mcu_sta_rate_ctrl_tlv(struct sk_buff *skb, struct ieee80211_vif *vif, struct ieee80211_link_sta *link_sta) { struct mt792x_vif *mvif = (struct mt792x_vif *)vif->drv_priv; struct ieee80211_bss_conf *link_conf; struct cfg80211_chan_def *chandef; struct sta_rec_ra_info *ra_info; struct mt792x_bss_conf *mconf; enum nl80211_band band; struct tlv *tlv; u16 supp_rates; link_conf = mt792x_vif_to_bss_conf(vif, link_sta->link_id); mconf = mt792x_vif_to_link(mvif, link_sta->link_id); chandef = mconf->mt76.ctx ? &mconf->mt76.ctx->def : &link_conf->chanreq.oper; band = chandef->chan->band; tlv = mt76_connac_mcu_add_tlv(skb, STA_REC_RA, sizeof(*ra_info)); ra_info = (struct sta_rec_ra_info *)tlv; supp_rates = link_sta->supp_rates[band]; if (band == NL80211_BAND_2GHZ) supp_rates = FIELD_PREP(RA_LEGACY_OFDM, supp_rates >> 4) | FIELD_PREP(RA_LEGACY_CCK, supp_rates & 0xf); else supp_rates = FIELD_PREP(RA_LEGACY_OFDM, supp_rates); ra_info->legacy = cpu_to_le16(supp_rates); if (link_sta->ht_cap.ht_supported) memcpy(ra_info->rx_mcs_bitmask, link_sta->ht_cap.mcs.rx_mask, HT_MCS_MASK_NUM); } static void mt7925_mcu_sta_eht_mld_tlv(struct sk_buff *skb, struct ieee80211_vif *vif, struct ieee80211_sta *sta) { struct mt792x_vif *mvif = (struct mt792x_vif *)vif->drv_priv; struct wiphy *wiphy = mvif->phy->mt76->hw->wiphy; const struct wiphy_iftype_ext_capab *ext_capa; struct sta_rec_eht_mld *eht_mld; struct tlv *tlv; u16 eml_cap; if (!ieee80211_vif_is_mld(vif)) return; tlv = mt76_connac_mcu_add_tlv(skb, STA_REC_EHT_MLD, sizeof(*eht_mld)); eht_mld = (struct sta_rec_eht_mld *)tlv; eht_mld->mld_type = 0xff; ext_capa = cfg80211_get_iftype_ext_capa(wiphy, ieee80211_vif_type_p2p(vif)); if (!ext_capa) return; eml_cap = (vif->cfg.eml_cap & (IEEE80211_EML_CAP_EMLSR_SUPP | IEEE80211_EML_CAP_TRANSITION_TIMEOUT)) | (ext_capa->eml_capabilities & (IEEE80211_EML_CAP_EMLSR_PADDING_DELAY | IEEE80211_EML_CAP_EMLSR_TRANSITION_DELAY)); if (eml_cap & IEEE80211_EML_CAP_EMLSR_SUPP) { eht_mld->eml_cap[0] = u16_get_bits(eml_cap, GENMASK(7, 0)); eht_mld->eml_cap[1] = u16_get_bits(eml_cap, GENMASK(15, 8)); } else { eht_mld->str_cap[0] = BIT(1); } } static void mt7925_mcu_sta_mld_tlv(struct sk_buff *skb, struct ieee80211_vif *vif, struct ieee80211_sta *sta) { struct mt792x_vif *mvif = (struct mt792x_vif *)vif->drv_priv; struct mt792x_sta *msta = (struct mt792x_sta *)sta->drv_priv; unsigned long valid = mvif->valid_links; struct mt792x_bss_conf *mconf; struct mt792x_link_sta *mlink; struct sta_rec_mld *mld; struct tlv *tlv; int i, cnt = 0; tlv = mt76_connac_mcu_add_tlv(skb, STA_REC_MLD, sizeof(*mld)); mld = (struct sta_rec_mld *)tlv; memcpy(mld->mac_addr, sta->addr, ETH_ALEN); mld->primary_id = cpu_to_le16(msta->deflink.wcid.idx); mld->wlan_id = cpu_to_le16(msta->deflink.wcid.idx); mld->link_num = min_t(u8, hweight16(mvif->valid_links), 2); for_each_set_bit(i, &valid, IEEE80211_MLD_MAX_NUM_LINKS) { if (cnt == mld->link_num) break; mconf = mt792x_vif_to_link(mvif, i); mlink = mt792x_sta_to_link(msta, i); mld->link[cnt].wlan_id = cpu_to_le16(mlink->wcid.idx); mld->link[cnt++].bss_idx = mconf->mt76.idx; if (mlink != &msta->deflink) mld->secondary_id = cpu_to_le16(mlink->wcid.idx); } } static void mt7925_mcu_sta_remove_tlv(struct sk_buff *skb) { struct sta_rec_remove *rem; struct tlv *tlv; tlv = mt76_connac_mcu_add_tlv(skb, 0x25, sizeof(*rem)); rem = (struct sta_rec_remove *)tlv; rem->action = 0; } static int mt7925_mcu_sta_cmd(struct mt76_phy *phy, struct mt76_sta_cmd_info *info) { struct mt792x_vif *mvif = (struct mt792x_vif *)info->vif->drv_priv; struct mt76_dev *dev = phy->dev; struct mt792x_bss_conf *mconf; struct sk_buff *skb; int conn_state; mconf = mt792x_vif_to_link(mvif, info->wcid->link_id); skb = __mt76_connac_mcu_alloc_sta_req(dev, &mconf->mt76, info->wcid, MT7925_STA_UPDATE_MAX_SIZE); if (IS_ERR(skb)) return PTR_ERR(skb); conn_state = info->enable ? CONN_STATE_PORT_SECURE : CONN_STATE_DISCONNECT; if (info->enable && info->link_sta) { mt76_connac_mcu_sta_basic_tlv(dev, skb, info->link_conf, info->link_sta, conn_state, info->newly); mt7925_mcu_sta_phy_tlv(skb, info->vif, info->link_sta); mt7925_mcu_sta_ht_tlv(skb, info->link_sta); mt7925_mcu_sta_vht_tlv(skb, info->link_sta); mt76_connac_mcu_sta_uapsd(skb, info->vif, info->link_sta->sta); mt7925_mcu_sta_amsdu_tlv(skb, info->vif, info->link_sta); mt7925_mcu_sta_he_tlv(skb, info->link_sta); mt7925_mcu_sta_he_6g_tlv(skb, info->link_sta); mt7925_mcu_sta_eht_tlv(skb, info->link_sta); mt7925_mcu_sta_rate_ctrl_tlv(skb, info->vif, info->link_sta); mt7925_mcu_sta_state_v2_tlv(phy, skb, info->link_sta, info->vif, info->rcpi, info->state); if (info->state != MT76_STA_INFO_STATE_NONE) { mt7925_mcu_sta_mld_tlv(skb, info->vif, info->link_sta->sta); mt7925_mcu_sta_eht_mld_tlv(skb, info->vif, info->link_sta->sta); } } if (!info->enable) { mt7925_mcu_sta_remove_tlv(skb); mt76_connac_mcu_add_tlv(skb, STA_REC_MLD_OFF, sizeof(struct tlv)); } else { mt7925_mcu_sta_hdr_trans_tlv(skb, info->vif, info->link_sta); } return mt76_mcu_skb_send_msg(dev, skb, info->cmd, true); } int mt7925_mcu_sta_update(struct mt792x_dev *dev, struct ieee80211_link_sta *link_sta, struct ieee80211_vif *vif, bool enable, enum mt76_sta_info_state state) { struct mt792x_vif *mvif = (struct mt792x_vif *)vif->drv_priv; int rssi = -ewma_rssi_read(&mvif->bss_conf.rssi); struct mt76_sta_cmd_info info = { .link_sta = link_sta, .vif = vif, .link_conf = &vif->bss_conf, .enable = enable, .cmd = MCU_UNI_CMD(STA_REC_UPDATE), .state = state, .offload_fw = true, .rcpi = to_rcpi(rssi), }; struct mt792x_sta *msta; struct mt792x_link_sta *mlink; if (link_sta) { msta = (struct mt792x_sta *)link_sta->sta->drv_priv; mlink = mt792x_sta_to_link(msta, link_sta->link_id); } info.wcid = link_sta ? &mlink->wcid : &mvif->sta.deflink.wcid; info.newly = state != MT76_STA_INFO_STATE_ASSOC; return mt7925_mcu_sta_cmd(&dev->mphy, &info); } int mt7925_mcu_set_beacon_filter(struct mt792x_dev *dev, struct ieee80211_vif *vif, bool enable) { #define MT7925_FIF_BIT_CLR BIT(1) #define MT7925_FIF_BIT_SET BIT(0) int err = 0; if (enable) { err = mt7925_mcu_uni_bss_bcnft(dev, &vif->bss_conf, true); if (err < 0) return err; return mt7925_mcu_set_rxfilter(dev, 0, MT7925_FIF_BIT_SET, MT_WF_RFCR_DROP_OTHER_BEACON); } err = mt7925_mcu_set_bss_pm(dev, &vif->bss_conf, false); if (err < 0) return err; return mt7925_mcu_set_rxfilter(dev, 0, MT7925_FIF_BIT_CLR, MT_WF_RFCR_DROP_OTHER_BEACON); } int mt7925_get_txpwr_info(struct mt792x_dev *dev, u8 band_idx, struct mt7925_txpwr *txpwr) { #define TX_POWER_SHOW_INFO 0x7 #define TXPOWER_ALL_RATE_POWER_INFO 0x2 struct mt7925_txpwr_event *event; struct mt7925_txpwr_req req = { .tag = cpu_to_le16(TX_POWER_SHOW_INFO), .len = cpu_to_le16(sizeof(req) - 4), .catg = TXPOWER_ALL_RATE_POWER_INFO, .band_idx = band_idx, }; struct sk_buff *skb; int ret; ret = mt76_mcu_send_and_get_msg(&dev->mt76, MCU_UNI_CMD(TXPOWER), &req, sizeof(req), true, &skb); if (ret) return ret; event = (struct mt7925_txpwr_event *)skb->data; memcpy(txpwr, &event->txpwr, sizeof(event->txpwr)); dev_kfree_skb(skb); return 0; } int mt7925_mcu_set_sniffer(struct mt792x_dev *dev, struct ieee80211_vif *vif, bool enable) { struct { struct { u8 band_idx; u8 pad[3]; } __packed hdr; struct sniffer_enable_tlv { __le16 tag; __le16 len; u8 enable; u8 pad[3]; } __packed enable; } __packed req = { .hdr = { .band_idx = 0, }, .enable = { .tag = cpu_to_le16(UNI_SNIFFER_ENABLE), .len = cpu_to_le16(sizeof(struct sniffer_enable_tlv)), .enable = enable, }, }; return mt76_mcu_send_msg(&dev->mt76, MCU_UNI_CMD(SNIFFER), &req, sizeof(req), true); } int mt7925_mcu_config_sniffer(struct mt792x_vif *vif, struct ieee80211_chanctx_conf *ctx) { struct mt76_phy *mphy = vif->phy->mt76; struct cfg80211_chan_def *chandef = ctx ? &ctx->def : &mphy->chandef; int freq1 = chandef->center_freq1, freq2 = chandef->center_freq2; static const u8 ch_band[] = { [NL80211_BAND_2GHZ] = 1, [NL80211_BAND_5GHZ] = 2, [NL80211_BAND_6GHZ] = 3, }; static const u8 ch_width[] = { [NL80211_CHAN_WIDTH_20_NOHT] = 0, [NL80211_CHAN_WIDTH_20] = 0, [NL80211_CHAN_WIDTH_40] = 0, [NL80211_CHAN_WIDTH_80] = 1, [NL80211_CHAN_WIDTH_160] = 2, [NL80211_CHAN_WIDTH_80P80] = 3, [NL80211_CHAN_WIDTH_5] = 4, [NL80211_CHAN_WIDTH_10] = 5, [NL80211_CHAN_WIDTH_320] = 6, }; struct { struct { u8 band_idx; u8 pad[3]; } __packed hdr; struct config_tlv { __le16 tag; __le16 len; u16 aid; u8 ch_band; u8 bw; u8 control_ch; u8 sco; u8 center_ch; u8 center_ch2; u8 drop_err; u8 pad[3]; } __packed tlv; } __packed req = { .hdr = { .band_idx = 0, }, .tlv = { .tag = cpu_to_le16(UNI_SNIFFER_CONFIG), .len = cpu_to_le16(sizeof(req.tlv)), .control_ch = chandef->chan->hw_value, .center_ch = ieee80211_frequency_to_channel(freq1), .drop_err = 1, }, }; if (chandef->chan->band < ARRAY_SIZE(ch_band)) req.tlv.ch_band = ch_band[chandef->chan->band]; if (chandef->width < ARRAY_SIZE(ch_width)) req.tlv.bw = ch_width[chandef->width]; if (freq2) req.tlv.center_ch2 = ieee80211_frequency_to_channel(freq2); if (req.tlv.control_ch < req.tlv.center_ch) req.tlv.sco = 1; /* SCA */ else if (req.tlv.control_ch > req.tlv.center_ch) req.tlv.sco = 3; /* SCB */ return mt76_mcu_send_msg(mphy->dev, MCU_UNI_CMD(SNIFFER), &req, sizeof(req), true); } int mt7925_mcu_uni_add_beacon_offload(struct mt792x_dev *dev, struct ieee80211_hw *hw, struct ieee80211_vif *vif, bool enable) { struct mt792x_vif *mvif = (struct mt792x_vif *)vif->drv_priv; struct ieee80211_mutable_offsets offs; struct { struct req_hdr { u8 bss_idx; u8 pad[3]; } __packed hdr; struct bcn_content_tlv { __le16 tag; __le16 len; __le16 tim_ie_pos; __le16 csa_ie_pos; __le16 bcc_ie_pos; /* 0: disable beacon offload * 1: enable beacon offload * 2: update probe respond offload */ u8 enable; /* 0: legacy format (TXD + payload) * 1: only cap field IE */ u8 type; __le16 pkt_len; u8 pkt[512]; } __packed beacon_tlv; } req = { .hdr = { .bss_idx = mvif->bss_conf.mt76.idx, }, .beacon_tlv = { .tag = cpu_to_le16(UNI_BSS_INFO_BCN_CONTENT), .len = cpu_to_le16(sizeof(struct bcn_content_tlv)), .enable = enable, .type = 1, }, }; struct sk_buff *skb; u8 cap_offs; /* support enable/update process only * disable flow would be handled in bss stop handler automatically */ if (!enable) return -EOPNOTSUPP; skb = ieee80211_beacon_get_template(mt76_hw(dev), vif, &offs, 0); if (!skb) return -EINVAL; cap_offs = offsetof(struct ieee80211_mgmt, u.beacon.capab_info); if (!skb_pull(skb, cap_offs)) { dev_err(dev->mt76.dev, "beacon format err\n"); dev_kfree_skb(skb); return -EINVAL; } if (skb->len > 512) { dev_err(dev->mt76.dev, "beacon size limit exceed\n"); dev_kfree_skb(skb); return -EINVAL; } memcpy(req.beacon_tlv.pkt, skb->data, skb->len); req.beacon_tlv.pkt_len = cpu_to_le16(skb->len); offs.tim_offset -= cap_offs; req.beacon_tlv.tim_ie_pos = cpu_to_le16(offs.tim_offset); if (offs.cntdwn_counter_offs[0]) { u16 csa_offs; csa_offs = offs.cntdwn_counter_offs[0] - cap_offs - 4; req.beacon_tlv.csa_ie_pos = cpu_to_le16(csa_offs); } dev_kfree_skb(skb); return mt76_mcu_send_msg(&dev->mt76, MCU_UNI_CMD(BSS_INFO_UPDATE), &req, sizeof(req), true); } static void mt7925_mcu_bss_rlm_tlv(struct sk_buff *skb, struct mt76_phy *phy, struct ieee80211_bss_conf *link_conf, struct ieee80211_chanctx_conf *ctx) { struct cfg80211_chan_def *chandef = ctx ? &ctx->def : &link_conf->chanreq.oper; int freq1 = chandef->center_freq1, freq2 = chandef->center_freq2; enum nl80211_band band = chandef->chan->band; struct bss_rlm_tlv *req; struct tlv *tlv; tlv = mt76_connac_mcu_add_tlv(skb, UNI_BSS_INFO_RLM, sizeof(*req)); req = (struct bss_rlm_tlv *)tlv; req->control_channel = chandef->chan->hw_value; req->center_chan = ieee80211_frequency_to_channel(freq1); req->center_chan2 = 0; req->tx_streams = hweight8(phy->antenna_mask); req->ht_op_info = 4; /* set HT 40M allowed */ req->rx_streams = hweight8(phy->antenna_mask); req->center_chan2 = 0; req->sco = 0; req->band = 1; switch (band) { case NL80211_BAND_2GHZ: req->band = 1; break; case NL80211_BAND_5GHZ: req->band = 2; break; case NL80211_BAND_6GHZ: req->band = 3; break; default: break; } switch (chandef->width) { case NL80211_CHAN_WIDTH_40: req->bw = CMD_CBW_40MHZ; break; case NL80211_CHAN_WIDTH_80: req->bw = CMD_CBW_80MHZ; break; case NL80211_CHAN_WIDTH_80P80: req->bw = CMD_CBW_8080MHZ; req->center_chan2 = ieee80211_frequency_to_channel(freq2); break; case NL80211_CHAN_WIDTH_160: req->bw = CMD_CBW_160MHZ; break; case NL80211_CHAN_WIDTH_5: req->bw = CMD_CBW_5MHZ; break; case NL80211_CHAN_WIDTH_10: req->bw = CMD_CBW_10MHZ; break; case NL80211_CHAN_WIDTH_20_NOHT: case NL80211_CHAN_WIDTH_20: default: req->bw = CMD_CBW_20MHZ; req->ht_op_info = 0; break; } if (req->control_channel < req->center_chan) req->sco = 1; /* SCA */ else if (req->control_channel > req->center_chan) req->sco = 3; /* SCB */ } static struct sk_buff * __mt7925_mcu_alloc_bss_req(struct mt76_dev *dev, struct mt76_vif_link *mvif, int len) { struct bss_req_hdr hdr = { .bss_idx = mvif->idx, }; struct sk_buff *skb; skb = mt76_mcu_msg_alloc(dev, NULL, len); if (!skb) return ERR_PTR(-ENOMEM); skb_put_data(skb, &hdr, sizeof(hdr)); return skb; } static void mt7925_mcu_bss_eht_tlv(struct sk_buff *skb, struct mt76_phy *phy, struct ieee80211_bss_conf *link_conf, struct ieee80211_chanctx_conf *ctx) { struct cfg80211_chan_def *chandef = ctx ? &ctx->def : &link_conf->chanreq.oper; struct bss_eht_tlv *req; struct tlv *tlv; tlv = mt76_connac_mcu_add_tlv(skb, UNI_BSS_INFO_EHT, sizeof(*req)); req = (struct bss_eht_tlv *)tlv; req->is_eth_dscb_present = chandef->punctured ? 1 : 0; req->eht_dis_sub_chan_bitmap = cpu_to_le16(chandef->punctured); } int mt7925_mcu_set_eht_pp(struct mt76_phy *phy, struct mt76_vif_link *mvif, struct ieee80211_bss_conf *link_conf, struct ieee80211_chanctx_conf *ctx) { struct sk_buff *skb; skb = __mt7925_mcu_alloc_bss_req(phy->dev, mvif, MT7925_BSS_UPDATE_MAX_SIZE); if (IS_ERR(skb)) return PTR_ERR(skb); mt7925_mcu_bss_eht_tlv(skb, phy, link_conf, ctx); return mt76_mcu_skb_send_msg(phy->dev, skb, MCU_UNI_CMD(BSS_INFO_UPDATE), true); } int mt7925_mcu_set_chctx(struct mt76_phy *phy, struct mt76_vif_link *mvif, struct ieee80211_bss_conf *link_conf, struct ieee80211_chanctx_conf *ctx) { struct sk_buff *skb; skb = __mt7925_mcu_alloc_bss_req(phy->dev, mvif, MT7925_BSS_UPDATE_MAX_SIZE); if (IS_ERR(skb)) return PTR_ERR(skb); mt7925_mcu_bss_rlm_tlv(skb, phy, link_conf, ctx); return mt76_mcu_skb_send_msg(phy->dev, skb, MCU_UNI_CMD(BSS_INFO_UPDATE), true); } static u8 mt7925_get_phy_mode_ext(struct mt76_phy *phy, struct ieee80211_vif *vif, enum nl80211_band band, struct ieee80211_link_sta *link_sta) { struct ieee80211_he_6ghz_capa *he_6ghz_capa; const struct ieee80211_sta_eht_cap *eht_cap; __le16 capa = 0; u8 mode = 0; if (link_sta) { he_6ghz_capa = &link_sta->he_6ghz_capa; eht_cap = &link_sta->eht_cap; } else { struct ieee80211_supported_band *sband; sband = phy->hw->wiphy->bands[band]; capa = ieee80211_get_he_6ghz_capa(sband, vif->type); he_6ghz_capa = (struct ieee80211_he_6ghz_capa *)&capa; eht_cap = ieee80211_get_eht_iftype_cap(sband, vif->type); } switch (band) { case NL80211_BAND_2GHZ: if (eht_cap && eht_cap->has_eht) mode |= PHY_MODE_BE_24G; break; case NL80211_BAND_5GHZ: if (eht_cap && eht_cap->has_eht) mode |= PHY_MODE_BE_5G; break; case NL80211_BAND_6GHZ: if (he_6ghz_capa && he_6ghz_capa->capa) mode |= PHY_MODE_AX_6G; if (eht_cap && eht_cap->has_eht) mode |= PHY_MODE_BE_6G; break; default: break; } return mode; } static void mt7925_mcu_bss_basic_tlv(struct sk_buff *skb, struct ieee80211_bss_conf *link_conf, struct ieee80211_link_sta *link_sta, struct ieee80211_chanctx_conf *ctx, struct mt76_phy *phy, u16 wlan_idx, bool enable) { struct ieee80211_vif *vif = link_conf->vif; struct mt792x_bss_conf *mconf = mt792x_link_conf_to_mconf(link_conf); struct cfg80211_chan_def *chandef = ctx ? &ctx->def : &link_conf->chanreq.oper; enum nl80211_band band = chandef->chan->band; struct mt76_connac_bss_basic_tlv *basic_req; struct mt792x_link_sta *mlink; struct tlv *tlv; int conn_type; u8 idx; tlv = mt76_connac_mcu_add_tlv(skb, UNI_BSS_INFO_BASIC, sizeof(*basic_req)); basic_req = (struct mt76_connac_bss_basic_tlv *)tlv; idx = mconf->mt76.omac_idx > EXT_BSSID_START ? HW_BSSID_0 : mconf->mt76.omac_idx; basic_req->hw_bss_idx = idx; basic_req->phymode_ext = mt7925_get_phy_mode_ext(phy, vif, band, link_sta); if (band == NL80211_BAND_2GHZ) basic_req->nonht_basic_phy = cpu_to_le16(PHY_TYPE_ERP_INDEX); else basic_req->nonht_basic_phy = cpu_to_le16(PHY_TYPE_OFDM_INDEX); memcpy(basic_req->bssid, link_conf->bssid, ETH_ALEN); basic_req->phymode = mt76_connac_get_phy_mode(phy, vif, band, link_sta); basic_req->bcn_interval = cpu_to_le16(link_conf->beacon_int); basic_req->dtim_period = link_conf->dtim_period; basic_req->bmc_tx_wlan_idx = cpu_to_le16(wlan_idx); basic_req->link_idx = mconf->mt76.idx; if (link_sta) { struct mt792x_sta *msta; msta = (struct mt792x_sta *)link_sta->sta->drv_priv; mlink = mt792x_sta_to_link(msta, link_sta->link_id); } else { mlink = &mconf->vif->sta.deflink; } basic_req->sta_idx = cpu_to_le16(mlink->wcid.idx); basic_req->omac_idx = mconf->mt76.omac_idx; basic_req->band_idx = mconf->mt76.band_idx; basic_req->wmm_idx = mconf->mt76.wmm_idx; basic_req->conn_state = !enable; switch (vif->type) { case NL80211_IFTYPE_MESH_POINT: case NL80211_IFTYPE_AP: if (vif->p2p) conn_type = CONNECTION_P2P_GO; else conn_type = CONNECTION_INFRA_AP; basic_req->conn_type = cpu_to_le32(conn_type); basic_req->active = enable; break; case NL80211_IFTYPE_STATION: if (vif->p2p) conn_type = CONNECTION_P2P_GC; else conn_type = CONNECTION_INFRA_STA; basic_req->conn_type = cpu_to_le32(conn_type); basic_req->active = true; break; case NL80211_IFTYPE_ADHOC: basic_req->conn_type = cpu_to_le32(CONNECTION_IBSS_ADHOC); basic_req->active = true; break; default: WARN_ON(1); break; } } static void mt7925_mcu_bss_sec_tlv(struct sk_buff *skb, struct ieee80211_bss_conf *link_conf) { struct mt792x_bss_conf *mconf = mt792x_link_conf_to_mconf(link_conf); struct mt76_vif_link *mvif = &mconf->mt76; struct bss_sec_tlv { __le16 tag; __le16 len; u8 mode; u8 status; u8 cipher; u8 __rsv; } __packed * sec; struct tlv *tlv; tlv = mt76_connac_mcu_add_tlv(skb, UNI_BSS_INFO_SEC, sizeof(*sec)); sec = (struct bss_sec_tlv *)tlv; switch (mvif->cipher) { case CONNAC3_CIPHER_GCMP_256: case CONNAC3_CIPHER_GCMP: sec->mode = MODE_WPA3_SAE; sec->status = 8; break; case CONNAC3_CIPHER_AES_CCMP: sec->mode = MODE_WPA2_PSK; sec->status = 6; break; case CONNAC3_CIPHER_TKIP: sec->mode = MODE_WPA2_PSK; sec->status = 4; break; case CONNAC3_CIPHER_WEP104: case CONNAC3_CIPHER_WEP40: sec->mode = MODE_SHARED; sec->status = 0; break; default: sec->mode = MODE_OPEN; sec->status = 1; break; } sec->cipher = mvif->cipher; } static void mt7925_mcu_bss_bmc_tlv(struct sk_buff *skb, struct mt792x_phy *phy, struct ieee80211_chanctx_conf *ctx, struct ieee80211_bss_conf *link_conf) { struct cfg80211_chan_def *chandef = ctx ? &ctx->def : &link_conf->chanreq.oper; struct mt792x_bss_conf *mconf = mt792x_link_conf_to_mconf(link_conf); enum nl80211_band band = chandef->chan->band; struct mt76_vif_link *mvif = &mconf->mt76; struct bss_rate_tlv *bmc; struct tlv *tlv; u8 idx = mvif->mcast_rates_idx ? mvif->mcast_rates_idx : mvif->basic_rates_idx; tlv = mt76_connac_mcu_add_tlv(skb, UNI_BSS_INFO_RATE, sizeof(*bmc)); bmc = (struct bss_rate_tlv *)tlv; if (band == NL80211_BAND_2GHZ) bmc->basic_rate = cpu_to_le16(HR_DSSS_ERP_BASIC_RATE); else bmc->basic_rate = cpu_to_le16(OFDM_BASIC_RATE); bmc->short_preamble = (band == NL80211_BAND_2GHZ); bmc->bc_fixed_rate = idx; bmc->mc_fixed_rate = idx; } static void mt7925_mcu_bss_mld_tlv(struct sk_buff *skb, struct ieee80211_bss_conf *link_conf) { struct ieee80211_vif *vif = link_conf->vif; struct mt792x_bss_conf *mconf = mt792x_link_conf_to_mconf(link_conf); struct mt792x_vif *mvif = (struct mt792x_vif *)link_conf->vif->drv_priv; struct mt792x_phy *phy = mvif->phy; struct bss_mld_tlv *mld; struct tlv *tlv; bool is_mld; is_mld = ieee80211_vif_is_mld(link_conf->vif) || (hweight16(mvif->valid_links) > 1); tlv = mt76_connac_mcu_add_tlv(skb, UNI_BSS_INFO_MLD, sizeof(*mld)); mld = (struct bss_mld_tlv *)tlv; mld->link_id = is_mld ? link_conf->link_id : 0xff; /* apply the index of the primary link */ mld->group_mld_id = is_mld ? mvif->bss_conf.mt76.idx : 0xff; mld->own_mld_id = mconf->mt76.idx + 32; mld->remap_idx = 0xff; if (phy->chip_cap & MT792x_CHIP_CAP_MLO_EML_EN) { mld->eml_enable = !!(link_conf->vif->cfg.eml_cap & IEEE80211_EML_CAP_EMLSR_SUPP); } else { mld->eml_enable = 0; } memcpy(mld->mac_addr, vif->addr, ETH_ALEN); } static void mt7925_mcu_bss_qos_tlv(struct sk_buff *skb, struct ieee80211_bss_conf *link_conf) { struct mt76_connac_bss_qos_tlv *qos; struct tlv *tlv; tlv = mt76_connac_mcu_add_tlv(skb, UNI_BSS_INFO_QBSS, sizeof(*qos)); qos = (struct mt76_connac_bss_qos_tlv *)tlv; qos->qos = link_conf->qos; } static void mt7925_mcu_bss_he_tlv(struct sk_buff *skb, struct ieee80211_bss_conf *link_conf, struct mt792x_phy *phy) { #define DEFAULT_HE_PE_DURATION 4 #define DEFAULT_HE_DURATION_RTS_THRES 1023 const struct ieee80211_sta_he_cap *cap; struct bss_info_uni_he *he; struct tlv *tlv; cap = mt76_connac_get_he_phy_cap(phy->mt76, link_conf->vif); tlv = mt76_connac_mcu_add_tlv(skb, UNI_BSS_INFO_HE_BASIC, sizeof(*he)); he = (struct bss_info_uni_he *)tlv; he->he_pe_duration = link_conf->htc_trig_based_pkt_ext; if (!he->he_pe_duration) he->he_pe_duration = DEFAULT_HE_PE_DURATION; he->he_rts_thres = cpu_to_le16(link_conf->frame_time_rts_th); if (!he->he_rts_thres) he->he_rts_thres = cpu_to_le16(DEFAULT_HE_DURATION_RTS_THRES); he->max_nss_mcs[CMD_HE_MCS_BW80] = cap->he_mcs_nss_supp.tx_mcs_80; he->max_nss_mcs[CMD_HE_MCS_BW160] = cap->he_mcs_nss_supp.tx_mcs_160; he->max_nss_mcs[CMD_HE_MCS_BW8080] = cap->he_mcs_nss_supp.tx_mcs_80p80; } static void mt7925_mcu_bss_color_tlv(struct sk_buff *skb, struct ieee80211_bss_conf *link_conf, bool enable) { struct bss_info_uni_bss_color *color; struct tlv *tlv; tlv = mt76_connac_mcu_add_tlv(skb, UNI_BSS_INFO_BSS_COLOR, sizeof(*color)); color = (struct bss_info_uni_bss_color *)tlv; color->enable = enable ? link_conf->he_bss_color.enabled : 0; color->bss_color = enable ? link_conf->he_bss_color.color : 0; } static void mt7925_mcu_bss_ifs_tlv(struct sk_buff *skb, struct ieee80211_bss_conf *link_conf) { struct mt792x_vif *mvif = (struct mt792x_vif *)link_conf->vif->drv_priv; struct mt792x_phy *phy = mvif->phy; struct bss_ifs_time_tlv *ifs_time; struct tlv *tlv; tlv = mt76_connac_mcu_add_tlv(skb, UNI_BSS_INFO_IFS_TIME, sizeof(*ifs_time)); ifs_time = (struct bss_ifs_time_tlv *)tlv; ifs_time->slot_valid = true; ifs_time->slot_time = cpu_to_le16(phy->slottime); } int mt7925_mcu_set_timing(struct mt792x_phy *phy, struct ieee80211_bss_conf *link_conf) { struct mt792x_bss_conf *mconf = mt792x_link_conf_to_mconf(link_conf); struct mt792x_dev *dev = phy->dev; struct sk_buff *skb; skb = __mt7925_mcu_alloc_bss_req(&dev->mt76, &mconf->mt76, MT7925_BSS_UPDATE_MAX_SIZE); if (IS_ERR(skb)) return PTR_ERR(skb); mt7925_mcu_bss_ifs_tlv(skb, link_conf); return mt76_mcu_skb_send_msg(&dev->mt76, skb, MCU_UNI_CMD(BSS_INFO_UPDATE), true); } void mt7925_mcu_del_dev(struct mt76_dev *mdev, struct ieee80211_vif *vif) { struct mt76_vif_link *mvif = (struct mt76_vif_link *)vif->drv_priv; struct { struct { u8 omac_idx; u8 band_idx; __le16 pad; } __packed hdr; struct req_tlv { __le16 tag; __le16 len; u8 active; u8 link_idx; /* hw link idx */ u8 omac_addr[ETH_ALEN]; } __packed tlv; } dev_req = { .tlv = { .tag = cpu_to_le16(DEV_INFO_ACTIVE), .len = cpu_to_le16(sizeof(struct req_tlv)), .active = true, }, }; struct { struct { u8 bss_idx; u8 pad[3]; } __packed hdr; struct mt76_connac_bss_basic_tlv basic; } basic_req = { .basic = { .tag = cpu_to_le16(UNI_BSS_INFO_BASIC), .len = cpu_to_le16(sizeof(struct mt76_connac_bss_basic_tlv)), .active = true, .conn_state = 1, }, }; dev_req.hdr.omac_idx = mvif->omac_idx; dev_req.hdr.band_idx = mvif->band_idx; basic_req.hdr.bss_idx = mvif->idx; basic_req.basic.omac_idx = mvif->omac_idx; basic_req.basic.band_idx = mvif->band_idx; basic_req.basic.link_idx = mvif->link_idx; mt76_mcu_send_msg(mdev, MCU_UNI_CMD(BSS_INFO_UPDATE), &basic_req, sizeof(basic_req), true); /* recovery omac address for the legacy interface */ memcpy(dev_req.tlv.omac_addr, vif->addr, ETH_ALEN); mt76_mcu_send_msg(mdev, MCU_UNI_CMD(DEV_INFO_UPDATE), &dev_req, sizeof(dev_req), true); } int mt7925_mcu_add_bss_info(struct mt792x_phy *phy, struct ieee80211_chanctx_conf *ctx, struct ieee80211_bss_conf *link_conf, struct ieee80211_link_sta *link_sta, int enable) { struct mt792x_vif *mvif = (struct mt792x_vif *)link_conf->vif->drv_priv; struct mt792x_bss_conf *mconf = mt792x_link_conf_to_mconf(link_conf); struct mt792x_dev *dev = phy->dev; struct mt792x_link_sta *mlink_bc; struct sk_buff *skb; skb = __mt7925_mcu_alloc_bss_req(&dev->mt76, &mconf->mt76, MT7925_BSS_UPDATE_MAX_SIZE); if (IS_ERR(skb)) return PTR_ERR(skb); mlink_bc = mt792x_sta_to_link(&mvif->sta, mconf->link_id); /* bss_basic must be first */ mt7925_mcu_bss_basic_tlv(skb, link_conf, link_sta, ctx, phy->mt76, mlink_bc->wcid.idx, enable); mt7925_mcu_bss_sec_tlv(skb, link_conf); mt7925_mcu_bss_bmc_tlv(skb, phy, ctx, link_conf); mt7925_mcu_bss_qos_tlv(skb, link_conf); mt7925_mcu_bss_mld_tlv(skb, link_conf); mt7925_mcu_bss_ifs_tlv(skb, link_conf); if (link_conf->he_support) { mt7925_mcu_bss_he_tlv(skb, link_conf, phy); mt7925_mcu_bss_color_tlv(skb, link_conf, enable); } if (enable) mt7925_mcu_bss_rlm_tlv(skb, phy->mt76, link_conf, ctx); return mt76_mcu_skb_send_msg(&dev->mt76, skb, MCU_UNI_CMD(BSS_INFO_UPDATE), true); } int mt7925_mcu_set_dbdc(struct mt76_phy *phy, bool enable) { struct mt76_dev *mdev = phy->dev; struct mbmc_conf_tlv *conf; struct mbmc_set_req *hdr; struct sk_buff *skb; struct tlv *tlv; int max_len, err; max_len = sizeof(*hdr) + sizeof(*conf); skb = mt76_mcu_msg_alloc(mdev, NULL, max_len); if (!skb) return -ENOMEM; hdr = (struct mbmc_set_req *)skb_put(skb, sizeof(*hdr)); tlv = mt76_connac_mcu_add_tlv(skb, UNI_MBMC_SETTING, sizeof(*conf)); conf = (struct mbmc_conf_tlv *)tlv; conf->mbmc_en = enable; conf->band = 0; /* unused */ err = mt76_mcu_skb_send_msg(mdev, skb, MCU_UNI_CMD(SET_DBDC_PARMS), true); return err; } static void mt7925_mcu_build_scan_ie_tlv(struct mt76_dev *mdev, struct sk_buff *skb, struct ieee80211_scan_ies *scan_ies) { u32 max_len = sizeof(struct scan_ie_tlv) + MT76_CONNAC_SCAN_IE_LEN; struct scan_ie_tlv *ie; enum nl80211_band i; struct tlv *tlv; const u8 *ies; u16 ies_len; for (i = 0; i <= NL80211_BAND_6GHZ; i++) { if (i == NL80211_BAND_60GHZ) continue; ies = scan_ies->ies[i]; ies_len = scan_ies->len[i]; if (!ies || !ies_len) continue; if (ies_len > max_len) return; tlv = mt76_connac_mcu_add_tlv(skb, UNI_SCAN_IE, sizeof(*ie) + ies_len); ie = (struct scan_ie_tlv *)tlv; memcpy(ie->ies, ies, ies_len); ie->ies_len = cpu_to_le16(ies_len); switch (i) { case NL80211_BAND_2GHZ: ie->band = 1; break; case NL80211_BAND_6GHZ: ie->band = 3; break; default: ie->band = 2; break; } max_len -= (sizeof(*ie) + ies_len); } } int mt7925_mcu_hw_scan(struct mt76_phy *phy, struct ieee80211_vif *vif, struct ieee80211_scan_request *scan_req) { struct mt76_vif_link *mvif = (struct mt76_vif_link *)vif->drv_priv; struct cfg80211_scan_request *sreq = &scan_req->req; int n_ssids = 0, err, i; struct ieee80211_channel **scan_list = sreq->channels; struct mt76_dev *mdev = phy->dev; struct mt76_connac_mcu_scan_channel *chan; struct sk_buff *skb; struct scan_hdr_tlv *hdr; struct scan_req_tlv *req; struct scan_ssid_tlv *ssid; struct scan_bssid_tlv *bssid; struct scan_chan_info_tlv *chan_info; struct scan_ie_tlv *ie; struct scan_misc_tlv *misc; struct tlv *tlv; int max_len; if (test_bit(MT76_HW_SCANNING, &phy->state)) return -EBUSY; max_len = sizeof(*hdr) + sizeof(*req) + sizeof(*ssid) + sizeof(*bssid) * MT7925_RNR_SCAN_MAX_BSSIDS + sizeof(*chan_info) + sizeof(*misc) + sizeof(*ie) + MT76_CONNAC_SCAN_IE_LEN; skb = mt76_mcu_msg_alloc(mdev, NULL, max_len); if (!skb) return -ENOMEM; set_bit(MT76_HW_SCANNING, &phy->state); mvif->scan_seq_num = (mvif->scan_seq_num + 1) & 0x7f; hdr = (struct scan_hdr_tlv *)skb_put(skb, sizeof(*hdr)); hdr->seq_num = mvif->scan_seq_num | mvif->band_idx << 7; hdr->bss_idx = mvif->idx; tlv = mt76_connac_mcu_add_tlv(skb, UNI_SCAN_REQ, sizeof(*req)); req = (struct scan_req_tlv *)tlv; req->scan_type = sreq->n_ssids ? 1 : 0; req->probe_req_num = sreq->n_ssids ? 2 : 0; tlv = mt76_connac_mcu_add_tlv(skb, UNI_SCAN_SSID, sizeof(*ssid)); ssid = (struct scan_ssid_tlv *)tlv; for (i = 0; i < sreq->n_ssids; i++) { if (!sreq->ssids[i].ssid_len) continue; if (i >= MT7925_RNR_SCAN_MAX_BSSIDS) break; ssid->ssids[n_ssids].ssid_len = cpu_to_le32(sreq->ssids[i].ssid_len); memcpy(ssid->ssids[n_ssids].ssid, sreq->ssids[i].ssid, sreq->ssids[i].ssid_len); n_ssids++; } ssid->ssid_type = n_ssids ? BIT(2) : BIT(0); ssid->ssids_num = n_ssids; if (sreq->n_6ghz_params) { u8 j; mt76_connac_mcu_build_rnr_scan_param(mdev, sreq); for (j = 0; j < mdev->rnr.bssid_num; j++) { if (j >= MT7925_RNR_SCAN_MAX_BSSIDS) break; tlv = mt76_connac_mcu_add_tlv(skb, UNI_SCAN_BSSID, sizeof(*bssid)); bssid = (struct scan_bssid_tlv *)tlv; ether_addr_copy(bssid->bssid, mdev->rnr.bssid[j]); bssid->match_ch = mdev->rnr.channel[j]; bssid->match_ssid_ind = MT7925_RNR_SCAN_MAX_BSSIDS; bssid->match_short_ssid_ind = MT7925_RNR_SCAN_MAX_BSSIDS; } req->scan_func |= SCAN_FUNC_RNR_SCAN; } else { tlv = mt76_connac_mcu_add_tlv(skb, UNI_SCAN_BSSID, sizeof(*bssid)); bssid = (struct scan_bssid_tlv *)tlv; ether_addr_copy(bssid->bssid, sreq->bssid); } tlv = mt76_connac_mcu_add_tlv(skb, UNI_SCAN_CHANNEL, sizeof(*chan_info)); chan_info = (struct scan_chan_info_tlv *)tlv; chan_info->channels_num = min_t(u8, sreq->n_channels, ARRAY_SIZE(chan_info->channels)); for (i = 0; i < chan_info->channels_num; i++) { chan = &chan_info->channels[i]; switch (scan_list[i]->band) { case NL80211_BAND_2GHZ: chan->band = 1; break; case NL80211_BAND_6GHZ: chan->band = 3; break; default: chan->band = 2; break; } chan->channel_num = scan_list[i]->hw_value; } chan_info->channel_type = sreq->n_channels ? 4 : 0; req->scan_func |= SCAN_FUNC_SPLIT_SCAN; tlv = mt76_connac_mcu_add_tlv(skb, UNI_SCAN_MISC, sizeof(*misc)); misc = (struct scan_misc_tlv *)tlv; if (sreq->flags & NL80211_SCAN_FLAG_RANDOM_ADDR) { get_random_mask_addr(misc->random_mac, sreq->mac_addr, sreq->mac_addr_mask); req->scan_func |= SCAN_FUNC_RANDOM_MAC; } /* Append scan probe IEs as the last tlv */ mt7925_mcu_build_scan_ie_tlv(mdev, skb, &scan_req->ies); err = mt76_mcu_skb_send_msg(mdev, skb, MCU_UNI_CMD(SCAN_REQ), true); if (err < 0) clear_bit(MT76_HW_SCANNING, &phy->state); return err; } EXPORT_SYMBOL_GPL(mt7925_mcu_hw_scan); int mt7925_mcu_sched_scan_req(struct mt76_phy *phy, struct ieee80211_vif *vif, struct cfg80211_sched_scan_request *sreq, struct ieee80211_scan_ies *ies) { struct mt76_vif_link *mvif = (struct mt76_vif_link *)vif->drv_priv; struct ieee80211_channel **scan_list = sreq->channels; struct mt76_connac_mcu_scan_channel *chan; struct mt76_dev *mdev = phy->dev; struct cfg80211_match_set *cfg_match; struct cfg80211_ssid *cfg_ssid; struct scan_hdr_tlv *hdr; struct scan_sched_req *req; struct scan_ssid_tlv *ssid; struct scan_chan_info_tlv *chan_info; struct scan_ie_tlv *ie; struct scan_sched_ssid_match_sets *match; struct sk_buff *skb; struct tlv *tlv; int i, max_len; max_len = sizeof(*hdr) + sizeof(*req) + sizeof(*ssid) + sizeof(*chan_info) + sizeof(*ie) + sizeof(*match); skb = mt76_mcu_msg_alloc(mdev, NULL, max_len); if (!skb) return -ENOMEM; mvif->scan_seq_num = (mvif->scan_seq_num + 1) & 0x7f; hdr = (struct scan_hdr_tlv *)skb_put(skb, sizeof(*hdr)); hdr->seq_num = mvif->scan_seq_num | mvif->band_idx << 7; hdr->bss_idx = mvif->idx; tlv = mt76_connac_mcu_add_tlv(skb, UNI_SCAN_SCHED_REQ, sizeof(*req)); req = (struct scan_sched_req *)tlv; req->version = 1; if (sreq->flags & NL80211_SCAN_FLAG_RANDOM_ADDR) req->scan_func |= SCAN_FUNC_RANDOM_MAC; req->intervals_num = sreq->n_scan_plans; for (i = 0; i < req->intervals_num; i++) req->intervals[i] = cpu_to_le16(sreq->scan_plans[i].interval); tlv = mt76_connac_mcu_add_tlv(skb, UNI_SCAN_SSID, sizeof(*ssid)); ssid = (struct scan_ssid_tlv *)tlv; ssid->ssids_num = sreq->n_ssids; ssid->ssid_type = BIT(2); for (i = 0; i < ssid->ssids_num; i++) { cfg_ssid = &sreq->ssids[i]; memcpy(ssid->ssids[i].ssid, cfg_ssid->ssid, cfg_ssid->ssid_len); ssid->ssids[i].ssid_len = cpu_to_le32(cfg_ssid->ssid_len); } tlv = mt76_connac_mcu_add_tlv(skb, UNI_SCAN_SSID_MATCH_SETS, sizeof(*match)); match = (struct scan_sched_ssid_match_sets *)tlv; match->match_num = sreq->n_match_sets; for (i = 0; i < match->match_num; i++) { cfg_match = &sreq->match_sets[i]; memcpy(match->match[i].ssid, cfg_match->ssid.ssid, cfg_match->ssid.ssid_len); match->match[i].rssi_th = cpu_to_le32(cfg_match->rssi_thold); match->match[i].ssid_len = cfg_match->ssid.ssid_len; } tlv = mt76_connac_mcu_add_tlv(skb, UNI_SCAN_CHANNEL, sizeof(*chan_info)); chan_info = (struct scan_chan_info_tlv *)tlv; chan_info->channels_num = min_t(u8, sreq->n_channels, ARRAY_SIZE(chan_info->channels)); for (i = 0; i < chan_info->channels_num; i++) { chan = &chan_info->channels[i]; switch (scan_list[i]->band) { case NL80211_BAND_2GHZ: chan->band = 1; break; case NL80211_BAND_6GHZ: chan->band = 3; break; default: chan->band = 2; break; } chan->channel_num = scan_list[i]->hw_value; } chan_info->channel_type = sreq->n_channels ? 4 : 0; /* Append scan probe IEs as the last tlv */ mt7925_mcu_build_scan_ie_tlv(mdev, skb, ies); return mt76_mcu_skb_send_msg(mdev, skb, MCU_UNI_CMD(SCAN_REQ), true); } EXPORT_SYMBOL_GPL(mt7925_mcu_sched_scan_req); int mt7925_mcu_sched_scan_enable(struct mt76_phy *phy, struct ieee80211_vif *vif, bool enable) { struct mt76_dev *mdev = phy->dev; struct scan_sched_enable *req; struct scan_hdr_tlv *hdr; struct sk_buff *skb; struct tlv *tlv; int max_len; max_len = sizeof(*hdr) + sizeof(*req); skb = mt76_mcu_msg_alloc(mdev, NULL, max_len); if (!skb) return -ENOMEM; hdr = (struct scan_hdr_tlv *)skb_put(skb, sizeof(*hdr)); hdr->seq_num = 0; hdr->bss_idx = 0; tlv = mt76_connac_mcu_add_tlv(skb, UNI_SCAN_SCHED_ENABLE, sizeof(*req)); req = (struct scan_sched_enable *)tlv; req->active = !enable; if (enable) set_bit(MT76_HW_SCHED_SCANNING, &phy->state); else clear_bit(MT76_HW_SCHED_SCANNING, &phy->state); return mt76_mcu_skb_send_msg(mdev, skb, MCU_UNI_CMD(SCAN_REQ), true); } int mt7925_mcu_cancel_hw_scan(struct mt76_phy *phy, struct ieee80211_vif *vif) { struct mt76_vif_link *mvif = (struct mt76_vif_link *)vif->drv_priv; struct { struct scan_hdr { u8 seq_num; u8 bss_idx; u8 pad[2]; } __packed hdr; struct scan_cancel_tlv { __le16 tag; __le16 len; u8 is_ext_channel; u8 rsv[3]; } __packed cancel; } req = { .hdr = { .seq_num = mvif->scan_seq_num, .bss_idx = mvif->idx, }, .cancel = { .tag = cpu_to_le16(UNI_SCAN_CANCEL), .len = cpu_to_le16(sizeof(struct scan_cancel_tlv)), }, }; if (test_and_clear_bit(MT76_HW_SCANNING, &phy->state)) { struct cfg80211_scan_info info = { .aborted = true, }; ieee80211_scan_completed(phy->hw, &info); } return mt76_mcu_send_msg(phy->dev, MCU_UNI_CMD(SCAN_REQ), &req, sizeof(req), true); } EXPORT_SYMBOL_GPL(mt7925_mcu_cancel_hw_scan); int mt7925_mcu_set_channel_domain(struct mt76_phy *phy) { int len, i, n_max_channels, n_2ch = 0, n_5ch = 0, n_6ch = 0; struct { struct { u8 alpha2[4]; /* regulatory_request.alpha2 */ u8 bw_2g; /* BW_20_40M 0 * BW_20M 1 * BW_20_40_80M 2 * BW_20_40_80_160M 3 * BW_20_40_80_8080M 4 */ u8 bw_5g; u8 bw_6g; u8 pad; } __packed hdr; struct n_chan { __le16 tag; __le16 len; u8 n_2ch; u8 n_5ch; u8 n_6ch; u8 pad; } __packed n_ch; } req = { .hdr = { .bw_2g = 0, .bw_5g = 3, /* BW_20_40_80_160M */ .bw_6g = 3, }, .n_ch = { .tag = cpu_to_le16(2), }, }; struct mt76_connac_mcu_chan { __le16 hw_value; __le16 pad; __le32 flags; } __packed channel; struct mt76_dev *dev = phy->dev; struct ieee80211_channel *chan; struct sk_buff *skb; n_max_channels = phy->sband_2g.sband.n_channels + phy->sband_5g.sband.n_channels + phy->sband_6g.sband.n_channels; len = sizeof(req) + n_max_channels * sizeof(channel); skb = mt76_mcu_msg_alloc(dev, NULL, len); if (!skb) return -ENOMEM; skb_reserve(skb, sizeof(req)); for (i = 0; i < phy->sband_2g.sband.n_channels; i++) { chan = &phy->sband_2g.sband.channels[i]; if (chan->flags & IEEE80211_CHAN_DISABLED) continue; channel.hw_value = cpu_to_le16(chan->hw_value); channel.flags = cpu_to_le32(chan->flags); channel.pad = 0; skb_put_data(skb, &channel, sizeof(channel)); n_2ch++; } for (i = 0; i < phy->sband_5g.sband.n_channels; i++) { chan = &phy->sband_5g.sband.channels[i]; if (chan->flags & IEEE80211_CHAN_DISABLED) continue; channel.hw_value = cpu_to_le16(chan->hw_value); channel.flags = cpu_to_le32(chan->flags); channel.pad = 0; skb_put_data(skb, &channel, sizeof(channel)); n_5ch++; } for (i = 0; i < phy->sband_6g.sband.n_channels; i++) { chan = &phy->sband_6g.sband.channels[i]; if (chan->flags & IEEE80211_CHAN_DISABLED) continue; channel.hw_value = cpu_to_le16(chan->hw_value); channel.flags = cpu_to_le32(chan->flags); channel.pad = 0; skb_put_data(skb, &channel, sizeof(channel)); n_6ch++; } BUILD_BUG_ON(sizeof(dev->alpha2) > sizeof(req.hdr.alpha2)); memcpy(req.hdr.alpha2, dev->alpha2, sizeof(dev->alpha2)); req.n_ch.n_2ch = n_2ch; req.n_ch.n_5ch = n_5ch; req.n_ch.n_6ch = n_6ch; len = sizeof(struct n_chan) + (n_2ch + n_5ch + n_6ch) * sizeof(channel); req.n_ch.len = cpu_to_le16(len); memcpy(__skb_push(skb, sizeof(req)), &req, sizeof(req)); return mt76_mcu_skb_send_msg(dev, skb, MCU_UNI_CMD(SET_DOMAIN_INFO), true); } EXPORT_SYMBOL_GPL(mt7925_mcu_set_channel_domain); static int __mt7925_mcu_set_clc(struct mt792x_dev *dev, u8 *alpha2, enum environment_cap env_cap, struct mt7925_clc *clc, u8 idx) { struct mt7925_clc_segment *seg; struct sk_buff *skb; struct { u8 rsv[4]; __le16 tag; __le16 len; u8 ver; u8 pad0; __le16 size; u8 idx; u8 env; u8 acpi_conf; u8 pad1; u8 alpha2[2]; u8 type[2]; u8 rsvd[64]; } __packed req = { .tag = cpu_to_le16(0x3), .len = cpu_to_le16(sizeof(req) - 4), .idx = idx, .env = env_cap, }; int ret, valid_cnt = 0; u8 *pos, *last_pos; if (!clc) return 0; req.ver = clc->ver; pos = clc->data + sizeof(*seg) * clc->t0.nr_seg; last_pos = clc->data + le32_to_cpu(*(__le32 *)(clc->data + 4)); while (pos < last_pos) { struct mt7925_clc_rule *rule = (struct mt7925_clc_rule *)pos; pos += sizeof(*rule); if (rule->alpha2[0] != alpha2[0] || rule->alpha2[1] != alpha2[1]) continue; seg = (struct mt7925_clc_segment *)clc->data + rule->seg_idx - 1; memcpy(req.alpha2, rule->alpha2, 2); memcpy(req.type, rule->type, 2); req.size = cpu_to_le16(seg->len); dev->phy.clc_chan_conf = clc->ver == 1 ? 0xff : rule->flag; skb = __mt76_mcu_msg_alloc(&dev->mt76, &req, le16_to_cpu(req.size) + sizeof(req), sizeof(req), GFP_KERNEL); if (!skb) return -ENOMEM; skb_put_data(skb, clc->data + seg->offset, seg->len); ret = mt76_mcu_skb_send_msg(&dev->mt76, skb, MCU_UNI_CMD(SET_POWER_LIMIT), true); if (ret < 0) return ret; valid_cnt++; } if (!valid_cnt) { dev->phy.clc_chan_conf = 0xff; return -ENOENT; } return 0; } int mt7925_mcu_set_clc(struct mt792x_dev *dev, u8 *alpha2, enum environment_cap env_cap) { struct mt792x_phy *phy = (struct mt792x_phy *)&dev->phy; int i, ret; /* submit all clc config */ for (i = 0; i < ARRAY_SIZE(phy->clc); i++) { if (i == MT792x_CLC_BE_CTRL) continue; ret = __mt7925_mcu_set_clc(dev, alpha2, env_cap, phy->clc[i], i); /* If no country found, set "00" as default */ if (ret == -ENOENT) ret = __mt7925_mcu_set_clc(dev, "00", ENVIRON_INDOOR, phy->clc[i], i); if (ret < 0) return ret; } return 0; } int mt7925_mcu_fill_message(struct mt76_dev *mdev, struct sk_buff *skb, int cmd, int *wait_seq) { int txd_len, mcu_cmd = FIELD_GET(__MCU_CMD_FIELD_ID, cmd); struct mt76_connac2_mcu_uni_txd *uni_txd; struct mt76_connac2_mcu_txd *mcu_txd; __le32 *txd; u32 val; u8 seq; /* TODO: make dynamic based on msg type */ mdev->mcu.timeout = 20 * HZ; seq = ++mdev->mcu.msg_seq & 0xf; if (!seq) seq = ++mdev->mcu.msg_seq & 0xf; if (cmd == MCU_CMD(FW_SCATTER)) goto exit; txd_len = cmd & __MCU_CMD_FIELD_UNI ? sizeof(*uni_txd) : sizeof(*mcu_txd); txd = (__le32 *)skb_push(skb, txd_len); val = FIELD_PREP(MT_TXD0_TX_BYTES, skb->len) | FIELD_PREP(MT_TXD0_PKT_FMT, MT_TX_TYPE_CMD) | FIELD_PREP(MT_TXD0_Q_IDX, MT_TX_MCU_PORT_RX_Q0); txd[0] = cpu_to_le32(val); val = FIELD_PREP(MT_TXD1_HDR_FORMAT, MT_HDR_FORMAT_CMD); txd[1] = cpu_to_le32(val); if (cmd & __MCU_CMD_FIELD_UNI) { uni_txd = (struct mt76_connac2_mcu_uni_txd *)txd; uni_txd->len = cpu_to_le16(skb->len - sizeof(uni_txd->txd)); uni_txd->cid = cpu_to_le16(mcu_cmd); uni_txd->s2d_index = MCU_S2D_H2N; uni_txd->pkt_type = MCU_PKT_ID; uni_txd->seq = seq; if (cmd & __MCU_CMD_FIELD_QUERY) uni_txd->option = MCU_CMD_UNI_QUERY_ACK; else uni_txd->option = MCU_CMD_UNI_EXT_ACK; if (cmd == MCU_UNI_CMD(HIF_CTRL) || cmd == MCU_UNI_CMD(CHIP_CONFIG)) uni_txd->option &= ~MCU_CMD_ACK; if (mcu_cmd == MCU_UNI_CMD_TESTMODE_CTRL || mcu_cmd == MCU_UNI_CMD_TESTMODE_RX_STAT) { if (cmd & __MCU_CMD_FIELD_QUERY) uni_txd->option = 0x2; else uni_txd->option = 0x6; } goto exit; } mcu_txd = (struct mt76_connac2_mcu_txd *)txd; mcu_txd->len = cpu_to_le16(skb->len - sizeof(mcu_txd->txd)); mcu_txd->pq_id = cpu_to_le16(MCU_PQ_ID(MT_TX_PORT_IDX_MCU, MT_TX_MCU_PORT_RX_Q0)); mcu_txd->pkt_type = MCU_PKT_ID; mcu_txd->seq = seq; mcu_txd->cid = mcu_cmd; mcu_txd->ext_cid = FIELD_GET(__MCU_CMD_FIELD_EXT_ID, cmd); if (mcu_txd->ext_cid || (cmd & __MCU_CMD_FIELD_CE)) { if (cmd & __MCU_CMD_FIELD_QUERY) mcu_txd->set_query = MCU_Q_QUERY; else mcu_txd->set_query = MCU_Q_SET; mcu_txd->ext_cid_ack = !!mcu_txd->ext_cid; } else { mcu_txd->set_query = MCU_Q_NA; } if (cmd & __MCU_CMD_FIELD_WA) mcu_txd->s2d_index = MCU_S2D_H2C; else mcu_txd->s2d_index = MCU_S2D_H2N; exit: if (wait_seq) *wait_seq = seq; return 0; } EXPORT_SYMBOL_GPL(mt7925_mcu_fill_message); int mt7925_mcu_set_rts_thresh(struct mt792x_phy *phy, u32 val) { struct { u8 band_idx; u8 _rsv[3]; __le16 tag; __le16 len; __le32 len_thresh; __le32 pkt_thresh; } __packed req = { .band_idx = phy->mt76->band_idx, .tag = cpu_to_le16(UNI_BAND_CONFIG_RTS_THRESHOLD), .len = cpu_to_le16(sizeof(req) - 4), .len_thresh = cpu_to_le32(val), .pkt_thresh = cpu_to_le32(0x2), }; return mt76_mcu_send_msg(&phy->dev->mt76, MCU_UNI_CMD(BAND_CONFIG), &req, sizeof(req), true); } int mt7925_mcu_set_radio_en(struct mt792x_phy *phy, bool enable) { struct { u8 band_idx; u8 _rsv[3]; __le16 tag; __le16 len; u8 enable; u8 _rsv2[3]; } __packed req = { .band_idx = phy->mt76->band_idx, .tag = cpu_to_le16(UNI_BAND_CONFIG_RADIO_ENABLE), .len = cpu_to_le16(sizeof(req) - 4), .enable = enable, }; return mt76_mcu_send_msg(&phy->dev->mt76, MCU_UNI_CMD(BAND_CONFIG), &req, sizeof(req), true); } static void mt7925_mcu_build_sku(struct mt76_dev *dev, s8 *sku, struct mt76_power_limits *limits, enum nl80211_band band) { int i, offset = sizeof(limits->cck); memset(sku, 127, MT_CONNAC3_SKU_POWER_LIMIT); if (band == NL80211_BAND_2GHZ) { /* cck */ memcpy(sku, limits->cck, sizeof(limits->cck)); } /* ofdm */ memcpy(&sku[offset], limits->ofdm, sizeof(limits->ofdm)); offset += (sizeof(limits->ofdm) * 5); /* ht */ for (i = 0; i < 2; i++) { memcpy(&sku[offset], limits->mcs[i], 8); offset += 8; } sku[offset++] = limits->mcs[0][0]; /* vht */ for (i = 0; i < ARRAY_SIZE(limits->mcs); i++) { memcpy(&sku[offset], limits->mcs[i], ARRAY_SIZE(limits->mcs[i])); offset += 12; } /* he */ for (i = 0; i < ARRAY_SIZE(limits->ru); i++) { memcpy(&sku[offset], limits->ru[i], ARRAY_SIZE(limits->ru[i])); offset += ARRAY_SIZE(limits->ru[i]); } /* eht */ for (i = 0; i < ARRAY_SIZE(limits->eht); i++) { memcpy(&sku[offset], limits->eht[i], ARRAY_SIZE(limits->eht[i])); offset += ARRAY_SIZE(limits->eht[i]); } } static int mt7925_mcu_rate_txpower_band(struct mt76_phy *phy, enum nl80211_band band) { int tx_power, n_chan, last_ch, err = 0, idx = 0; int i, sku_len, batch_size, batch_len = 3; struct mt76_dev *dev = phy->dev; static const u8 chan_list_2ghz[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14 }; static const u8 chan_list_5ghz[] = { 36, 38, 40, 42, 44, 46, 48, 50, 52, 54, 56, 58, 60, 62, 64, 100, 102, 104, 106, 108, 110, 112, 114, 116, 118, 120, 122, 124, 126, 128, 132, 134, 136, 138, 140, 142, 144, 149, 151, 153, 155, 157, 159, 161, 165, 167 }; static const u8 chan_list_6ghz[] = { 1, 3, 5, 7, 9, 11, 13, 15, 17, 19, 21, 23, 25, 27, 29, 33, 35, 37, 39, 41, 43, 45, 47, 49, 51, 53, 55, 57, 59, 61, 65, 67, 69, 71, 73, 75, 77, 79, 81, 83, 85, 87, 89, 91, 93, 97, 99, 101, 103, 105, 107, 109, 111, 113, 115, 117, 119, 121, 123, 125, 129, 131, 133, 135, 137, 139, 141, 143, 145, 147, 149, 151, 153, 155, 157, 161, 163, 165, 167, 169, 171, 173, 175, 177, 179, 181, 183, 185, 187, 189, 193, 195, 197, 199, 201, 203, 205, 207, 209, 211, 213, 215, 217, 219, 221, 225, 227, 229, 233 }; struct mt76_power_limits *limits; struct mt7925_sku_tlv *sku_tlbv; const u8 *ch_list; sku_len = sizeof(*sku_tlbv); tx_power = 2 * phy->hw->conf.power_level; if (!tx_power) tx_power = 127; if (band == NL80211_BAND_2GHZ) { n_chan = ARRAY_SIZE(chan_list_2ghz); ch_list = chan_list_2ghz; last_ch = chan_list_2ghz[ARRAY_SIZE(chan_list_2ghz) - 1]; } else if (band == NL80211_BAND_6GHZ) { n_chan = ARRAY_SIZE(chan_list_6ghz); ch_list = chan_list_6ghz; last_ch = chan_list_6ghz[ARRAY_SIZE(chan_list_6ghz) - 1]; } else { n_chan = ARRAY_SIZE(chan_list_5ghz); ch_list = chan_list_5ghz; last_ch = chan_list_5ghz[ARRAY_SIZE(chan_list_5ghz) - 1]; } batch_size = DIV_ROUND_UP(n_chan, batch_len); limits = devm_kmalloc(dev->dev, sizeof(*limits), GFP_KERNEL); if (!limits) return -ENOMEM; sku_tlbv = devm_kmalloc(dev->dev, sku_len, GFP_KERNEL); if (!sku_tlbv) { devm_kfree(dev->dev, limits); return -ENOMEM; } for (i = 0; i < batch_size; i++) { struct mt7925_tx_power_limit_tlv *tx_power_tlv; int j, msg_len, num_ch; struct sk_buff *skb; num_ch = i == batch_size - 1 ? n_chan % batch_len : batch_len; msg_len = sizeof(*tx_power_tlv) + num_ch * sku_len; skb = mt76_mcu_msg_alloc(dev, NULL, msg_len); if (!skb) { err = -ENOMEM; goto out; } tx_power_tlv = (struct mt7925_tx_power_limit_tlv *) skb_put(skb, sizeof(*tx_power_tlv)); BUILD_BUG_ON(sizeof(dev->alpha2) > sizeof(tx_power_tlv->alpha2)); memcpy(tx_power_tlv->alpha2, dev->alpha2, sizeof(dev->alpha2)); tx_power_tlv->n_chan = num_ch; tx_power_tlv->tag = cpu_to_le16(0x1); tx_power_tlv->len = cpu_to_le16(sizeof(*tx_power_tlv)); switch (band) { case NL80211_BAND_2GHZ: tx_power_tlv->band = 1; break; case NL80211_BAND_6GHZ: tx_power_tlv->band = 3; break; default: tx_power_tlv->band = 2; break; } for (j = 0; j < num_ch; j++, idx++) { struct ieee80211_channel chan = { .hw_value = ch_list[idx], .band = band, }; s8 reg_power, sar_power; reg_power = mt76_connac_get_ch_power(phy, &chan, tx_power); sar_power = mt76_get_sar_power(phy, &chan, reg_power); mt76_get_rate_power_limits(phy, &chan, limits, sar_power); tx_power_tlv->last_msg = ch_list[idx] == last_ch; sku_tlbv->channel = ch_list[idx]; mt7925_mcu_build_sku(dev, sku_tlbv->pwr_limit, limits, band); skb_put_data(skb, sku_tlbv, sku_len); } err = mt76_mcu_skb_send_msg(dev, skb, MCU_UNI_CMD(SET_POWER_LIMIT), true); if (err < 0) goto out; } out: devm_kfree(dev->dev, sku_tlbv); devm_kfree(dev->dev, limits); return err; } int mt7925_mcu_set_rate_txpower(struct mt76_phy *phy) { int err; if (phy->cap.has_2ghz) { err = mt7925_mcu_rate_txpower_band(phy, NL80211_BAND_2GHZ); if (err < 0) return err; } if (phy->cap.has_5ghz) { err = mt7925_mcu_rate_txpower_band(phy, NL80211_BAND_5GHZ); if (err < 0) return err; } if (phy->cap.has_6ghz) { err = mt7925_mcu_rate_txpower_band(phy, NL80211_BAND_6GHZ); if (err < 0) return err; } return 0; } int mt7925_mcu_wf_rf_pin_ctrl(struct mt792x_phy *phy) { #define UNI_CMD_RADIO_STATUS_GET 0 struct mt792x_dev *dev = phy->dev; struct sk_buff *skb; int ret; struct { __le16 tag; __le16 len; u8 rsv[4]; } __packed req = { .tag = UNI_CMD_RADIO_STATUS_GET, .len = cpu_to_le16(sizeof(req)), }; struct mt7925_radio_status_event { __le16 tag; __le16 len; u8 data; u8 rsv[3]; } __packed *status; ret = mt76_mcu_send_and_get_msg(&dev->mt76, MCU_UNI_CMD(RADIO_STATUS), &req, sizeof(req), true, &skb); if (ret) return ret; skb_pull(skb, sizeof(struct tlv)); status = (struct mt7925_radio_status_event *)skb->data; ret = status->data; dev_kfree_skb(skb); return ret; } int mt7925_mcu_set_rxfilter(struct mt792x_dev *dev, u32 fif, u8 bit_op, u32 bit_map) { struct mt792x_phy *phy = &dev->phy; struct { u8 band_idx; u8 rsv1[3]; __le16 tag; __le16 len; u8 mode; u8 rsv2[3]; __le32 fif; __le32 bit_map; /* bit_* for bitmap update */ u8 bit_op; u8 pad[51]; } __packed req = { .band_idx = phy->mt76->band_idx, .tag = cpu_to_le16(UNI_BAND_CONFIG_SET_MAC80211_RX_FILTER), .len = cpu_to_le16(sizeof(req) - 4), .mode = fif ? 0 : 1, .fif = cpu_to_le32(fif), .bit_map = cpu_to_le32(bit_map), .bit_op = bit_op, }; return mt76_mcu_send_msg(&phy->dev->mt76, MCU_UNI_CMD(BAND_CONFIG), &req, sizeof(req), true); } diff --git a/sys/contrib/dev/mediatek/mt76/mt7925/pci.c b/sys/contrib/dev/mediatek/mt76/mt7925/pci.c index 3417fc5db6a9..e34f99abd16c 100644 --- a/sys/contrib/dev/mediatek/mt76/mt7925/pci.c +++ b/sys/contrib/dev/mediatek/mt76/mt7925/pci.c @@ -1,626 +1,630 @@ // SPDX-License-Identifier: ISC /* Copyright (C) 2023 MediaTek Inc. */ +#if defined(__FreeBSD__) +#define LINUXKPI_PARAM_PREFIX mt7925_pci_ +#endif + #include #include #include #include "mt7925.h" #include "mac.h" #include "mcu.h" #include "../dma.h" static const struct pci_device_id mt7925_pci_device_table[] = { { PCI_DEVICE(PCI_VENDOR_ID_MEDIATEK, 0x7925), .driver_data = (kernel_ulong_t)MT7925_FIRMWARE_WM }, { PCI_DEVICE(PCI_VENDOR_ID_MEDIATEK, 0x0717), .driver_data = (kernel_ulong_t)MT7925_FIRMWARE_WM }, { }, }; static bool mt7925_disable_aspm; module_param_named(disable_aspm, mt7925_disable_aspm, bool, 0644); MODULE_PARM_DESC(disable_aspm, "disable PCI ASPM support"); static int mt7925e_init_reset(struct mt792x_dev *dev) { return mt792x_wpdma_reset(dev, true); } static void mt7925e_unregister_device(struct mt792x_dev *dev) { int i; struct mt76_connac_pm *pm = &dev->pm; struct ieee80211_hw *hw = mt76_hw(dev); if (dev->phy.chip_cap & MT792x_CHIP_CAP_WF_RF_PIN_CTRL_EVT_EN) wiphy_rfkill_stop_polling(hw->wiphy); cancel_work_sync(&dev->init_work); mt76_unregister_device(&dev->mt76); mt76_for_each_q_rx(&dev->mt76, i) napi_disable(&dev->mt76.napi[i]); cancel_delayed_work_sync(&pm->ps_work); cancel_work_sync(&pm->wake_work); cancel_work_sync(&dev->reset_work); mt7925_tx_token_put(dev); __mt792x_mcu_drv_pmctrl(dev); mt792x_dma_cleanup(dev); mt792x_wfsys_reset(dev); skb_queue_purge(&dev->mt76.mcu.res_q); tasklet_disable(&dev->mt76.irq_tasklet); } static void mt7925_reg_remap_restore(struct mt792x_dev *dev) { /* remap to ori status */ if (unlikely(dev->backup_l1)) { dev->bus_ops->wr(&dev->mt76, MT_HIF_REMAP_L1, dev->backup_l1); dev->backup_l1 = 0; } if (dev->backup_l2) { dev->bus_ops->wr(&dev->mt76, MT_HIF_REMAP_L2, dev->backup_l2); dev->backup_l2 = 0; } } static u32 mt7925_reg_map_l1(struct mt792x_dev *dev, u32 addr) { u32 offset = FIELD_GET(MT_HIF_REMAP_L1_OFFSET, addr); u32 base = FIELD_GET(MT_HIF_REMAP_L1_BASE, addr); dev->backup_l1 = dev->bus_ops->rr(&dev->mt76, MT_HIF_REMAP_L1); dev->bus_ops->rmw(&dev->mt76, MT_HIF_REMAP_L1, MT_HIF_REMAP_L1_MASK, FIELD_PREP(MT_HIF_REMAP_L1_MASK, base)); /* use read to push write */ dev->bus_ops->rr(&dev->mt76, MT_HIF_REMAP_L1); return MT_HIF_REMAP_BASE_L1 + offset; } static u32 mt7925_reg_map_l2(struct mt792x_dev *dev, u32 addr) { u32 base = FIELD_GET(MT_HIF_REMAP_L1_BASE, MT_HIF_REMAP_BASE_L2); dev->backup_l2 = dev->bus_ops->rr(&dev->mt76, MT_HIF_REMAP_L1); dev->bus_ops->rmw(&dev->mt76, MT_HIF_REMAP_L1, MT_HIF_REMAP_L1_MASK, FIELD_PREP(MT_HIF_REMAP_L1_MASK, base)); dev->bus_ops->wr(&dev->mt76, MT_HIF_REMAP_L2, addr); /* use read to push write */ dev->bus_ops->rr(&dev->mt76, MT_HIF_REMAP_L1); return MT_HIF_REMAP_BASE_L1; } static u32 __mt7925_reg_addr(struct mt792x_dev *dev, u32 addr) { static const struct mt76_connac_reg_map fixed_map[] = { { 0x830c0000, 0x000000, 0x0001000 }, /* WF_MCU_BUS_CR_REMAP */ { 0x54000000, 0x002000, 0x0001000 }, /* WFDMA PCIE0 MCU DMA0 */ { 0x55000000, 0x003000, 0x0001000 }, /* WFDMA PCIE0 MCU DMA1 */ { 0x56000000, 0x004000, 0x0001000 }, /* WFDMA reserved */ { 0x57000000, 0x005000, 0x0001000 }, /* WFDMA MCU wrap CR */ { 0x58000000, 0x006000, 0x0001000 }, /* WFDMA PCIE1 MCU DMA0 (MEM_DMA) */ { 0x59000000, 0x007000, 0x0001000 }, /* WFDMA PCIE1 MCU DMA1 */ { 0x820c0000, 0x008000, 0x0004000 }, /* WF_UMAC_TOP (PLE) */ { 0x820c8000, 0x00c000, 0x0002000 }, /* WF_UMAC_TOP (PSE) */ { 0x820cc000, 0x00e000, 0x0002000 }, /* WF_UMAC_TOP (PP) */ { 0x74030000, 0x010000, 0x0001000 }, /* PCIe MAC */ { 0x820e0000, 0x020000, 0x0000400 }, /* WF_LMAC_TOP BN0 (WF_CFG) */ { 0x820e1000, 0x020400, 0x0000200 }, /* WF_LMAC_TOP BN0 (WF_TRB) */ { 0x820e2000, 0x020800, 0x0000400 }, /* WF_LMAC_TOP BN0 (WF_AGG) */ { 0x820e3000, 0x020c00, 0x0000400 }, /* WF_LMAC_TOP BN0 (WF_ARB) */ { 0x820e4000, 0x021000, 0x0000400 }, /* WF_LMAC_TOP BN0 (WF_TMAC) */ { 0x820e5000, 0x021400, 0x0000800 }, /* WF_LMAC_TOP BN0 (WF_RMAC) */ { 0x820ce000, 0x021c00, 0x0000200 }, /* WF_LMAC_TOP (WF_SEC) */ { 0x820e7000, 0x021e00, 0x0000200 }, /* WF_LMAC_TOP BN0 (WF_DMA) */ { 0x820cf000, 0x022000, 0x0001000 }, /* WF_LMAC_TOP (WF_PF) */ { 0x820e9000, 0x023400, 0x0000200 }, /* WF_LMAC_TOP BN0 (WF_WTBLOFF) */ { 0x820ea000, 0x024000, 0x0000200 }, /* WF_LMAC_TOP BN0 (WF_ETBF) */ { 0x820eb000, 0x024200, 0x0000400 }, /* WF_LMAC_TOP BN0 (WF_LPON) */ { 0x820ec000, 0x024600, 0x0000200 }, /* WF_LMAC_TOP BN0 (WF_INT) */ { 0x820ed000, 0x024800, 0x0000800 }, /* WF_LMAC_TOP BN0 (WF_MIB) */ { 0x820ca000, 0x026000, 0x0002000 }, /* WF_LMAC_TOP BN0 (WF_MUCOP) */ { 0x820d0000, 0x030000, 0x0010000 }, /* WF_LMAC_TOP (WF_WTBLON) */ { 0x40000000, 0x070000, 0x0010000 }, /* WF_UMAC_SYSRAM */ { 0x00400000, 0x080000, 0x0010000 }, /* WF_MCU_SYSRAM */ { 0x00410000, 0x090000, 0x0010000 }, /* WF_MCU_SYSRAM (configure register) */ { 0x820f0000, 0x0a0000, 0x0000400 }, /* WF_LMAC_TOP BN1 (WF_CFG) */ { 0x820f1000, 0x0a0600, 0x0000200 }, /* WF_LMAC_TOP BN1 (WF_TRB) */ { 0x820f2000, 0x0a0800, 0x0000400 }, /* WF_LMAC_TOP BN1 (WF_AGG) */ { 0x820f3000, 0x0a0c00, 0x0000400 }, /* WF_LMAC_TOP BN1 (WF_ARB) */ { 0x820f4000, 0x0a1000, 0x0000400 }, /* WF_LMAC_TOP BN1 (WF_TMAC) */ { 0x820f5000, 0x0a1400, 0x0000800 }, /* WF_LMAC_TOP BN1 (WF_RMAC) */ { 0x820f7000, 0x0a1e00, 0x0000200 }, /* WF_LMAC_TOP BN1 (WF_DMA) */ { 0x820f9000, 0x0a3400, 0x0000200 }, /* WF_LMAC_TOP BN1 (WF_WTBLOFF) */ { 0x820fa000, 0x0a4000, 0x0000200 }, /* WF_LMAC_TOP BN1 (WF_ETBF) */ { 0x820fb000, 0x0a4200, 0x0000400 }, /* WF_LMAC_TOP BN1 (WF_LPON) */ { 0x820fc000, 0x0a4600, 0x0000200 }, /* WF_LMAC_TOP BN1 (WF_INT) */ { 0x820fd000, 0x0a4800, 0x0000800 }, /* WF_LMAC_TOP BN1 (WF_MIB) */ { 0x820c4000, 0x0a8000, 0x0004000 }, /* WF_LMAC_TOP BN1 (WF_MUCOP) */ { 0x820b0000, 0x0ae000, 0x0001000 }, /* [APB2] WFSYS_ON */ { 0x80020000, 0x0b0000, 0x0010000 }, /* WF_TOP_MISC_OFF */ { 0x81020000, 0x0c0000, 0x0010000 }, /* WF_TOP_MISC_ON */ { 0x7c020000, 0x0d0000, 0x0010000 }, /* CONN_INFRA, wfdma */ { 0x7c060000, 0x0e0000, 0x0010000 }, /* CONN_INFRA, conn_host_csr_top */ { 0x7c000000, 0x0f0000, 0x0010000 }, /* CONN_INFRA */ { 0x70020000, 0x1f0000, 0x0010000 }, /* Reserved for CBTOP, can't switch */ { 0x7c500000, 0x060000, 0x2000000 }, /* remap */ { 0x0, 0x0, 0x0 } /* End */ }; int i; if (addr < 0x200000) return addr; mt7925_reg_remap_restore(dev); for (i = 0; i < ARRAY_SIZE(fixed_map); i++) { u32 ofs; if (addr < fixed_map[i].phys) continue; ofs = addr - fixed_map[i].phys; if (ofs > fixed_map[i].size) continue; return fixed_map[i].maps + ofs; } if ((addr >= 0x18000000 && addr < 0x18c00000) || (addr >= 0x70000000 && addr < 0x78000000) || (addr >= 0x7c000000 && addr < 0x7c400000)) return mt7925_reg_map_l1(dev, addr); return mt7925_reg_map_l2(dev, addr); } static u32 mt7925_rr(struct mt76_dev *mdev, u32 offset) { struct mt792x_dev *dev = container_of(mdev, struct mt792x_dev, mt76); u32 addr = __mt7925_reg_addr(dev, offset); return dev->bus_ops->rr(mdev, addr); } static void mt7925_wr(struct mt76_dev *mdev, u32 offset, u32 val) { struct mt792x_dev *dev = container_of(mdev, struct mt792x_dev, mt76); u32 addr = __mt7925_reg_addr(dev, offset); dev->bus_ops->wr(mdev, addr, val); } static u32 mt7925_rmw(struct mt76_dev *mdev, u32 offset, u32 mask, u32 val) { struct mt792x_dev *dev = container_of(mdev, struct mt792x_dev, mt76); u32 addr = __mt7925_reg_addr(dev, offset); return dev->bus_ops->rmw(mdev, addr, mask, val); } static int mt7925_dma_init(struct mt792x_dev *dev) { int ret; mt76_dma_attach(&dev->mt76); ret = mt792x_dma_disable(dev, true); if (ret) return ret; /* init tx queue */ ret = mt76_connac_init_tx_queues(dev->phy.mt76, MT7925_TXQ_BAND0, MT7925_TX_RING_SIZE, MT_TX_RING_BASE, NULL, 0); if (ret) return ret; mt76_wr(dev, MT_WFDMA0_TX_RING0_EXT_CTRL, 0x4); /* command to WM */ ret = mt76_init_mcu_queue(&dev->mt76, MT_MCUQ_WM, MT7925_TXQ_MCU_WM, MT7925_TX_MCU_RING_SIZE, MT_TX_RING_BASE); if (ret) return ret; /* firmware download */ ret = mt76_init_mcu_queue(&dev->mt76, MT_MCUQ_FWDL, MT7925_TXQ_FWDL, MT7925_TX_FWDL_RING_SIZE, MT_TX_RING_BASE); if (ret) return ret; /* rx event */ ret = mt76_queue_alloc(dev, &dev->mt76.q_rx[MT_RXQ_MCU], MT7925_RXQ_MCU_WM, MT7925_RX_MCU_RING_SIZE, MT_RX_BUF_SIZE, MT_RX_EVENT_RING_BASE); if (ret) return ret; /* rx data */ ret = mt76_queue_alloc(dev, &dev->mt76.q_rx[MT_RXQ_MAIN], MT7925_RXQ_BAND0, MT7925_RX_RING_SIZE, MT_RX_BUF_SIZE, MT_RX_DATA_RING_BASE); if (ret) return ret; ret = mt76_init_queues(dev, mt792x_poll_rx); if (ret < 0) return ret; netif_napi_add_tx(dev->mt76.tx_napi_dev, &dev->mt76.tx_napi, mt792x_poll_tx); napi_enable(&dev->mt76.tx_napi); return mt792x_dma_enable(dev); } static int mt7925_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) { static const struct mt76_driver_ops drv_ops = { /* txwi_size = txd size + txp size */ .txwi_size = MT_TXD_SIZE + sizeof(struct mt76_connac_hw_txp), .drv_flags = MT_DRV_TXWI_NO_FREE | MT_DRV_HW_MGMT_TXQ | MT_DRV_AMSDU_OFFLOAD, .survey_flags = SURVEY_INFO_TIME_TX | SURVEY_INFO_TIME_RX | SURVEY_INFO_TIME_BSS_RX, .token_size = MT7925_TOKEN_SIZE, .tx_prepare_skb = mt7925e_tx_prepare_skb, .tx_complete_skb = mt76_connac_tx_complete_skb, .rx_check = mt7925_rx_check, .rx_skb = mt7925_queue_rx_skb, .rx_poll_complete = mt792x_rx_poll_complete, .sta_add = mt7925_mac_sta_add, .sta_event = mt7925_mac_sta_event, .sta_remove = mt7925_mac_sta_remove, .update_survey = mt792x_update_channel, }; static const struct mt792x_hif_ops mt7925_pcie_ops = { .init_reset = mt7925e_init_reset, .reset = mt7925e_mac_reset, .mcu_init = mt7925e_mcu_init, .drv_own = mt792xe_mcu_drv_pmctrl, .fw_own = mt792xe_mcu_fw_pmctrl, }; static const struct mt792x_irq_map irq_map = { .host_irq_enable = MT_WFDMA0_HOST_INT_ENA, .tx = { .all_complete_mask = MT_INT_TX_DONE_ALL, .mcu_complete_mask = MT_INT_TX_DONE_MCU, }, .rx = { .data_complete_mask = HOST_RX_DONE_INT_ENA2, .wm_complete_mask = HOST_RX_DONE_INT_ENA0, }, }; struct ieee80211_ops *ops; struct mt76_bus_ops *bus_ops; struct mt792x_dev *dev; struct mt76_dev *mdev; u8 features; int ret; u16 cmd; ret = pcim_enable_device(pdev); if (ret) return ret; ret = pcim_iomap_regions(pdev, BIT(0), pci_name(pdev)); if (ret) return ret; pci_read_config_word(pdev, PCI_COMMAND, &cmd); if (!(cmd & PCI_COMMAND_MEMORY)) { cmd |= PCI_COMMAND_MEMORY; pci_write_config_word(pdev, PCI_COMMAND, cmd); } pci_set_master(pdev); ret = pci_alloc_irq_vectors(pdev, 1, 1, PCI_IRQ_ALL_TYPES); if (ret < 0) return ret; ret = dma_set_mask(&pdev->dev, DMA_BIT_MASK(32)); if (ret) goto err_free_pci_vec; if (mt7925_disable_aspm) mt76_pci_disable_aspm(pdev); ops = mt792x_get_mac80211_ops(&pdev->dev, &mt7925_ops, (void *)id->driver_data, &features); if (!ops) { ret = -ENOMEM; goto err_free_pci_vec; } mdev = mt76_alloc_device(&pdev->dev, sizeof(*dev), ops, &drv_ops); if (!mdev) { ret = -ENOMEM; goto err_free_pci_vec; } pci_set_drvdata(pdev, mdev); dev = container_of(mdev, struct mt792x_dev, mt76); dev->fw_features = features; dev->hif_ops = &mt7925_pcie_ops; dev->irq_map = &irq_map; mt76_mmio_init(&dev->mt76, pcim_iomap_table(pdev)[0]); tasklet_init(&mdev->irq_tasklet, mt792x_irq_tasklet, (unsigned long)dev); dev->phy.dev = dev; dev->phy.mt76 = &dev->mt76.phy; dev->mt76.phy.priv = &dev->phy; dev->bus_ops = dev->mt76.bus; bus_ops = devm_kmemdup(dev->mt76.dev, dev->bus_ops, sizeof(*bus_ops), GFP_KERNEL); if (!bus_ops) { ret = -ENOMEM; goto err_free_dev; } bus_ops->rr = mt7925_rr; bus_ops->wr = mt7925_wr; bus_ops->rmw = mt7925_rmw; dev->mt76.bus = bus_ops; if (!mt7925_disable_aspm && mt76_pci_aspm_supported(pdev)) dev->aspm_supported = true; ret = __mt792x_mcu_fw_pmctrl(dev); if (ret) goto err_free_dev; ret = __mt792xe_mcu_drv_pmctrl(dev); if (ret) goto err_free_dev; mdev->rev = (mt76_rr(dev, MT_HW_CHIPID) << 16) | (mt76_rr(dev, MT_HW_REV) & 0xff); dev_info(mdev->dev, "ASIC revision: %04x\n", mdev->rev); mt76_rmw_field(dev, MT_HW_EMI_CTL, MT_HW_EMI_CTL_SLPPROT_EN, 1); ret = mt792x_wfsys_reset(dev); if (ret) goto err_free_dev; mt76_wr(dev, irq_map.host_irq_enable, 0); mt76_wr(dev, MT_PCIE_MAC_INT_ENABLE, 0xff); ret = devm_request_irq(mdev->dev, pdev->irq, mt792x_irq_handler, IRQF_SHARED, KBUILD_MODNAME, dev); if (ret) goto err_free_dev; ret = mt7925_dma_init(dev); if (ret) goto err_free_irq; ret = mt7925_register_device(dev); if (ret) goto err_free_irq; return 0; err_free_irq: devm_free_irq(&pdev->dev, pdev->irq, dev); err_free_dev: mt76_free_device(&dev->mt76); err_free_pci_vec: pci_free_irq_vectors(pdev); return ret; } static void mt7925_pci_remove(struct pci_dev *pdev) { struct mt76_dev *mdev = pci_get_drvdata(pdev); struct mt792x_dev *dev = container_of(mdev, struct mt792x_dev, mt76); mt7925e_unregister_device(dev); set_bit(MT76_REMOVED, &mdev->phy.state); devm_free_irq(&pdev->dev, pdev->irq, dev); mt76_free_device(&dev->mt76); pci_free_irq_vectors(pdev); } #if !defined(__FreeBSD__) || defined(CONFIG_PM_SLEEP) static int mt7925_pci_suspend(struct device *device) { struct pci_dev *pdev = to_pci_dev(device); struct mt76_dev *mdev = pci_get_drvdata(pdev); struct mt792x_dev *dev = container_of(mdev, struct mt792x_dev, mt76); struct mt76_connac_pm *pm = &dev->pm; int i, err, ret; pm->suspended = true; dev->hif_resumed = false; flush_work(&dev->reset_work); cancel_delayed_work_sync(&pm->ps_work); cancel_work_sync(&pm->wake_work); mt7925_roc_abort_sync(dev); err = mt792x_mcu_drv_pmctrl(dev); if (err < 0) goto restore_suspend; wait_event_timeout(dev->wait, !dev->regd_in_progress, 5 * HZ); /* always enable deep sleep during suspend to reduce * power consumption */ mt7925_mcu_set_deep_sleep(dev, true); mt76_connac_mcu_set_hif_suspend(mdev, true, false); ret = wait_event_timeout(dev->wait, dev->hif_idle, 3 * HZ); if (!ret) { err = -ETIMEDOUT; goto restore_suspend; } napi_disable(&mdev->tx_napi); mt76_worker_disable(&mdev->tx_worker); mt76_for_each_q_rx(mdev, i) { napi_disable(&mdev->napi[i]); } /* wait until dma is idle */ mt76_poll(dev, MT_WFDMA0_GLO_CFG, MT_WFDMA0_GLO_CFG_TX_DMA_BUSY | MT_WFDMA0_GLO_CFG_RX_DMA_BUSY, 0, 1000); /* put dma disabled */ mt76_clear(dev, MT_WFDMA0_GLO_CFG, MT_WFDMA0_GLO_CFG_TX_DMA_EN | MT_WFDMA0_GLO_CFG_RX_DMA_EN); /* disable interrupt */ mt76_wr(dev, dev->irq_map->host_irq_enable, 0); mt76_wr(dev, MT_PCIE_MAC_INT_ENABLE, 0x0); synchronize_irq(pdev->irq); tasklet_kill(&mdev->irq_tasklet); err = mt792x_mcu_fw_pmctrl(dev); if (err) goto restore_napi; return 0; restore_napi: mt76_for_each_q_rx(mdev, i) { napi_enable(&mdev->napi[i]); } napi_enable(&mdev->tx_napi); if (!pm->ds_enable) mt7925_mcu_set_deep_sleep(dev, false); mt76_connac_mcu_set_hif_suspend(mdev, false, false); ret = wait_event_timeout(dev->wait, dev->hif_resumed, 3 * HZ); if (!ret) err = -ETIMEDOUT; restore_suspend: pm->suspended = false; if (err < 0) mt792x_reset(&dev->mt76); return err; } static int mt7925_pci_resume(struct device *device) { struct pci_dev *pdev = to_pci_dev(device); struct mt76_dev *mdev = pci_get_drvdata(pdev); struct mt792x_dev *dev = container_of(mdev, struct mt792x_dev, mt76); struct mt76_connac_pm *pm = &dev->pm; int i, err, ret; dev->hif_idle = false; err = mt792x_mcu_drv_pmctrl(dev); if (err < 0) goto failed; mt792x_wpdma_reinit_cond(dev); /* enable interrupt */ mt76_wr(dev, MT_PCIE_MAC_INT_ENABLE, 0xff); mt76_connac_irq_enable(&dev->mt76, dev->irq_map->tx.all_complete_mask | MT_INT_RX_DONE_ALL | MT_INT_MCU_CMD); mt76_set(dev, MT_MCU2HOST_SW_INT_ENA, MT_MCU_CMD_WAKE_RX_PCIE); /* put dma enabled */ mt76_set(dev, MT_WFDMA0_GLO_CFG, MT_WFDMA0_GLO_CFG_TX_DMA_EN | MT_WFDMA0_GLO_CFG_RX_DMA_EN); mt76_worker_enable(&mdev->tx_worker); mt76_for_each_q_rx(mdev, i) { napi_enable(&mdev->napi[i]); } napi_enable(&mdev->tx_napi); local_bh_disable(); mt76_for_each_q_rx(mdev, i) { napi_schedule(&mdev->napi[i]); } napi_schedule(&mdev->tx_napi); local_bh_enable(); mt76_connac_mcu_set_hif_suspend(mdev, false, false); ret = wait_event_timeout(dev->wait, dev->hif_resumed, 3 * HZ); if (!ret) { err = -ETIMEDOUT; goto failed; } /* restore previous ds setting */ if (!pm->ds_enable) mt7925_mcu_set_deep_sleep(dev, false); mt7925_regd_update(dev); failed: pm->suspended = false; if (err < 0) mt792x_reset(&dev->mt76); return err; } #endif static void mt7925_pci_shutdown(struct pci_dev *pdev) { mt7925_pci_remove(pdev); } static DEFINE_SIMPLE_DEV_PM_OPS(mt7925_pm_ops, mt7925_pci_suspend, mt7925_pci_resume); static struct pci_driver mt7925_pci_driver = { .name = KBUILD_MODNAME, .id_table = mt7925_pci_device_table, .probe = mt7925_pci_probe, .remove = mt7925_pci_remove, .shutdown = mt7925_pci_shutdown, .driver.pm = pm_sleep_ptr(&mt7925_pm_ops), }; module_pci_driver(mt7925_pci_driver); MODULE_DEVICE_TABLE(pci, mt7925_pci_device_table); MODULE_FIRMWARE(MT7925_FIRMWARE_WM); MODULE_FIRMWARE(MT7925_ROM_PATCH); MODULE_AUTHOR("Deren Wu "); MODULE_AUTHOR("Lorenzo Bianconi "); MODULE_DESCRIPTION("MediaTek MT7925E (PCIe) wireless driver"); MODULE_LICENSE("Dual BSD/GPL"); #if defined(__FreeBSD__) MODULE_VERSION(mt7925_pci, 1); MODULE_DEPEND(mt7925_pci, linuxkpi, 1, 1, 1); MODULE_DEPEND(mt7925_pci, linuxkpi_wlan, 1, 1, 1); MODULE_DEPEND(mt7925_pci, mt76_core, 1, 1, 1); #endif diff --git a/sys/contrib/dev/mediatek/mt76/mt7996/coredump.c b/sys/contrib/dev/mediatek/mt76/mt7996/coredump.c index 303d6e80a666..433846d2319e 100644 --- a/sys/contrib/dev/mediatek/mt76/mt7996/coredump.c +++ b/sys/contrib/dev/mediatek/mt76/mt7996/coredump.c @@ -1,268 +1,272 @@ // SPDX-License-Identifier: ISC /* Copyright (C) 2023 MediaTek Inc. */ +#if defined(__FreeBSD__) +#define LINUXKPI_PARAM_PREFIX mt7996_ +#endif + #include #include #include #include #include "coredump.h" static bool coredump_memdump; module_param(coredump_memdump, bool, 0644); MODULE_PARM_DESC(coredump_memdump, "Optional ability to dump firmware memory"); static const struct mt7996_mem_region mt7996_mem_regions[] = { { .start = 0x00800000, .len = 0x0004ffff, .name = "ULM0", }, { .start = 0x00900000, .len = 0x00037fff, .name = "ULM1", }, { .start = 0x02200000, .len = 0x0003ffff, .name = "ULM2", }, { .start = 0x00400000, .len = 0x00067fff, .name = "SRAM", }, { .start = 0xe0000000, .len = 0x0015ffff, .name = "CRAM0", }, { .start = 0xe0160000, .len = 0x0011bfff, .name = "CRAM1", }, }; const struct mt7996_mem_region* mt7996_coredump_get_mem_layout(struct mt7996_dev *dev, u32 *num) { switch (mt76_chip(&dev->mt76)) { case MT7996_DEVICE_ID: case MT7996_DEVICE_ID_2: *num = ARRAY_SIZE(mt7996_mem_regions); return &mt7996_mem_regions[0]; default: return NULL; } } static int mt7996_coredump_get_mem_size(struct mt7996_dev *dev) { const struct mt7996_mem_region *mem_region; size_t size = 0; u32 num; int i; mem_region = mt7996_coredump_get_mem_layout(dev, &num); if (!mem_region) return 0; for (i = 0; i < num; i++) { size += mem_region->len; mem_region++; } /* reserve space for the headers */ size += num * sizeof(struct mt7996_mem_hdr); /* make sure it is aligned 4 bytes for debug message print out */ size = ALIGN(size, 4); return size; } struct mt7996_crash_data *mt7996_coredump_new(struct mt7996_dev *dev) { struct mt7996_crash_data *crash_data = dev->coredump.crash_data; lockdep_assert_held(&dev->dump_mutex); if (coredump_memdump && !mt76_poll_msec(dev, MT_FW_DUMP_STATE, 0x3, 0x2, 500)) return NULL; guid_gen(&crash_data->guid); ktime_get_real_ts64(&crash_data->timestamp); return crash_data; } static void mt7996_coredump_fw_state(struct mt7996_dev *dev, struct mt7996_coredump *dump, bool *exception) { u32 count; count = mt76_rr(dev, MT_FW_ASSERT_CNT); /* normal mode: driver can manually trigger assertĀ for detail info */ if (!count) strscpy(dump->fw_state, "normal", sizeof(dump->fw_state)); else strscpy(dump->fw_state, "exception", sizeof(dump->fw_state)); *exception = !!count; } static void mt7996_coredump_fw_stack(struct mt7996_dev *dev, struct mt7996_coredump *dump, bool exception) { u32 oldest, i, idx; strscpy(dump->pc_current, "program counter", sizeof(dump->pc_current)); /* 0: WM PC log output */ mt76_wr(dev, MT_CONN_DBG_CTL_OUT_SEL, 0); /* choose 33th PC log buffer to read current PC index */ mt76_wr(dev, MT_CONN_DBG_CTL_PC_LOG_SEL, 0x3f); /* read current PC */ dump->pc_stack[0] = mt76_rr(dev, MT_CONN_DBG_CTL_PC_LOG); /* stop call stack record */ if (!exception) { mt76_clear(dev, MT_MCU_WM_EXCP_PC_CTRL, BIT(0)); mt76_clear(dev, MT_MCU_WM_EXCP_LR_CTRL, BIT(0)); } oldest = (u32)mt76_get_field(dev, MT_MCU_WM_EXCP_PC_CTRL, GENMASK(20, 16)) + 2; for (i = 0; i < 16; i++) { idx = ((oldest + 2 * i + 1) % 32); dump->pc_stack[i + 1] = mt76_rr(dev, MT_MCU_WM_EXCP_PC_LOG + idx * 4); } oldest = (u32)mt76_get_field(dev, MT_MCU_WM_EXCP_LR_CTRL, GENMASK(20, 16)) + 2; for (i = 0; i < 16; i++) { idx = ((oldest + 2 * i + 1) % 32); dump->lr_stack[i] = mt76_rr(dev, MT_MCU_WM_EXCP_LR_LOG + idx * 4); } /* start call stack record */ if (!exception) { mt76_set(dev, MT_MCU_WM_EXCP_PC_CTRL, BIT(0)); mt76_set(dev, MT_MCU_WM_EXCP_LR_CTRL, BIT(0)); } } static struct mt7996_coredump *mt7996_coredump_build(struct mt7996_dev *dev) { struct mt7996_crash_data *crash_data = dev->coredump.crash_data; struct mt7996_coredump *dump; struct mt7996_coredump_mem *dump_mem; size_t len, sofar = 0, hdr_len = sizeof(*dump); unsigned char *buf; bool exception; len = hdr_len; if (coredump_memdump && crash_data->memdump_buf_len) len += sizeof(*dump_mem) + crash_data->memdump_buf_len; sofar += hdr_len; /* this is going to get big when we start dumping memory and such, * so go ahead and use vmalloc. */ buf = vzalloc(len); if (!buf) return NULL; mutex_lock(&dev->dump_mutex); dump = (struct mt7996_coredump *)(buf); dump->len = len; /* plain text */ strscpy(dump->magic, "mt76-crash-dump", sizeof(dump->magic)); strscpy(dump->kernel, init_utsname()->release, sizeof(dump->kernel)); strscpy(dump->fw_ver, dev->mt76.hw->wiphy->fw_version, sizeof(dump->fw_ver)); guid_copy(&dump->guid, &crash_data->guid); dump->tv_sec = crash_data->timestamp.tv_sec; dump->tv_nsec = crash_data->timestamp.tv_nsec; dump->device_id = mt76_chip(&dev->mt76); mt7996_coredump_fw_state(dev, dump, &exception); mt7996_coredump_fw_stack(dev, dump, exception); /* gather memory content */ dump_mem = (struct mt7996_coredump_mem *)(buf + sofar); dump_mem->len = crash_data->memdump_buf_len; if (coredump_memdump && crash_data->memdump_buf_len) memcpy(dump_mem->data, crash_data->memdump_buf, crash_data->memdump_buf_len); mutex_unlock(&dev->dump_mutex); return dump; } int mt7996_coredump_submit(struct mt7996_dev *dev) { struct mt7996_coredump *dump; dump = mt7996_coredump_build(dev); if (!dump) { dev_warn(dev->mt76.dev, "no crash dump data found\n"); return -ENODATA; } dev_coredumpv(dev->mt76.dev, dump, dump->len, GFP_KERNEL); return 0; } int mt7996_coredump_register(struct mt7996_dev *dev) { struct mt7996_crash_data *crash_data; crash_data = vzalloc(sizeof(*dev->coredump.crash_data)); if (!crash_data) return -ENOMEM; dev->coredump.crash_data = crash_data; if (coredump_memdump) { crash_data->memdump_buf_len = mt7996_coredump_get_mem_size(dev); if (!crash_data->memdump_buf_len) /* no memory content */ return 0; crash_data->memdump_buf = vzalloc(crash_data->memdump_buf_len); if (!crash_data->memdump_buf) { vfree(crash_data); return -ENOMEM; } } return 0; } void mt7996_coredump_unregister(struct mt7996_dev *dev) { if (dev->coredump.crash_data->memdump_buf) { vfree(dev->coredump.crash_data->memdump_buf); dev->coredump.crash_data->memdump_buf = NULL; dev->coredump.crash_data->memdump_buf_len = 0; } vfree(dev->coredump.crash_data); dev->coredump.crash_data = NULL; } diff --git a/sys/contrib/dev/mediatek/mt76/mt7996/mcu.c b/sys/contrib/dev/mediatek/mt76/mt7996/mcu.c index 5099aa2004b7..443cd21a32b8 100644 --- a/sys/contrib/dev/mediatek/mt76/mt7996/mcu.c +++ b/sys/contrib/dev/mediatek/mt76/mt7996/mcu.c @@ -1,4902 +1,4906 @@ // SPDX-License-Identifier: ISC /* * Copyright (C) 2022 MediaTek Inc. */ +#if defined(__FreeBSD__) +#define LINUXKPI_PARAM_PREFIX mt7996_ +#endif + #include #include #include "mt7996.h" #include "mcu.h" #include "mac.h" #include "eeprom.h" #define fw_name(_dev, name, ...) ({ \ char *_fw; \ switch (mt76_chip(&(_dev)->mt76)) { \ case MT7992_DEVICE_ID: \ switch ((_dev)->var.type) { \ case MT7992_VAR_TYPE_23: \ _fw = MT7992_##name##_23; \ break; \ default: \ _fw = MT7992_##name; \ } \ break; \ case MT7990_DEVICE_ID: \ _fw = MT7990_##name; \ break; \ case MT7996_DEVICE_ID: \ default: \ switch ((_dev)->var.type) { \ case MT7996_VAR_TYPE_233: \ _fw = MT7996_##name##_233; \ break; \ default: \ _fw = MT7996_##name; \ } \ break; \ } \ _fw; \ }) struct mt7996_patch_hdr { char build_date[16]; char platform[4]; __be32 hw_sw_ver; __be32 patch_ver; __be16 checksum; u16 reserved; struct { __be32 patch_ver; __be32 subsys; __be32 feature; __be32 n_region; __be32 crc; u32 reserved[11]; } desc; } __packed; struct mt7996_patch_sec { __be32 type; __be32 offs; __be32 size; union { __be32 spec[13]; struct { __be32 addr; __be32 len; __be32 sec_key_idx; __be32 align_len; u32 reserved[9]; } info; }; } __packed; struct mt7996_fw_trailer { u8 chip_id; u8 eco_code; u8 n_region; u8 format_ver; u8 format_flag; u8 reserved[2]; char fw_ver[10]; char build_date[15]; u32 crc; } __packed; struct mt7996_fw_region { __le32 decomp_crc; __le32 decomp_len; __le32 decomp_blk_sz; u8 reserved[4]; __le32 addr; __le32 len; u8 feature_set; u8 reserved1[15]; } __packed; #define MCU_PATCH_ADDRESS 0x200000 #define HE_PHY(p, c) u8_get_bits(c, IEEE80211_HE_PHY_##p) #define HE_MAC(m, c) u8_get_bits(c, IEEE80211_HE_MAC_##m) #define EHT_PHY(p, c) u8_get_bits(c, IEEE80211_EHT_PHY_##p) static bool sr_scene_detect = true; module_param(sr_scene_detect, bool, 0644); MODULE_PARM_DESC(sr_scene_detect, "Enable firmware scene detection algorithm"); static u8 mt7996_mcu_get_sta_nss(u16 mcs_map) { u8 nss; for (nss = 8; nss > 0; nss--) { u8 nss_mcs = (mcs_map >> (2 * (nss - 1))) & 3; if (nss_mcs != IEEE80211_VHT_MCS_NOT_SUPPORTED) break; } return nss - 1; } static void mt7996_mcu_set_sta_he_mcs(struct ieee80211_link_sta *link_sta, struct mt7996_vif_link *link, __le16 *he_mcs, u16 mcs_map) { int nss, max_nss = link_sta->rx_nss > 3 ? 4 : link_sta->rx_nss; enum nl80211_band band = link->phy->mt76->chandef.chan->band; const u16 *mask = link->bitrate_mask.control[band].he_mcs; for (nss = 0; nss < max_nss; nss++) { int mcs; switch ((mcs_map >> (2 * nss)) & 0x3) { case IEEE80211_HE_MCS_SUPPORT_0_11: mcs = GENMASK(11, 0); break; case IEEE80211_HE_MCS_SUPPORT_0_9: mcs = GENMASK(9, 0); break; case IEEE80211_HE_MCS_SUPPORT_0_7: mcs = GENMASK(7, 0); break; default: mcs = 0; } mcs = mcs ? fls(mcs & mask[nss]) - 1 : -1; switch (mcs) { case 0 ... 7: mcs = IEEE80211_HE_MCS_SUPPORT_0_7; break; case 8 ... 9: mcs = IEEE80211_HE_MCS_SUPPORT_0_9; break; case 10 ... 11: mcs = IEEE80211_HE_MCS_SUPPORT_0_11; break; default: mcs = IEEE80211_HE_MCS_NOT_SUPPORTED; break; } mcs_map &= ~(0x3 << (nss * 2)); mcs_map |= mcs << (nss * 2); } *he_mcs = cpu_to_le16(mcs_map); } static void mt7996_mcu_set_sta_vht_mcs(struct ieee80211_link_sta *link_sta, __le16 *vht_mcs, const u16 *mask) { u16 mcs, mcs_map = le16_to_cpu(link_sta->vht_cap.vht_mcs.rx_mcs_map); int nss, max_nss = link_sta->rx_nss > 3 ? 4 : link_sta->rx_nss; for (nss = 0; nss < max_nss; nss++, mcs_map >>= 2) { switch (mcs_map & 0x3) { case IEEE80211_VHT_MCS_SUPPORT_0_9: mcs = GENMASK(9, 0); break; case IEEE80211_VHT_MCS_SUPPORT_0_8: mcs = GENMASK(8, 0); break; case IEEE80211_VHT_MCS_SUPPORT_0_7: mcs = GENMASK(7, 0); break; default: mcs = 0; } vht_mcs[nss] = cpu_to_le16(mcs & mask[nss]); } } static void mt7996_mcu_set_sta_ht_mcs(struct ieee80211_link_sta *link_sta, u8 *ht_mcs, const u8 *mask) { int nss, max_nss = link_sta->rx_nss > 3 ? 4 : link_sta->rx_nss; for (nss = 0; nss < max_nss; nss++) ht_mcs[nss] = link_sta->ht_cap.mcs.rx_mask[nss] & mask[nss]; } static int mt7996_mcu_parse_response(struct mt76_dev *mdev, int cmd, struct sk_buff *skb, int seq) { struct mt7996_mcu_rxd *rxd; struct mt7996_mcu_uni_event *event; int mcu_cmd = FIELD_GET(__MCU_CMD_FIELD_ID, cmd); int ret = 0; if (!skb) { dev_err(mdev->dev, "Message %08x (seq %d) timeout\n", cmd, seq); return -ETIMEDOUT; } rxd = (struct mt7996_mcu_rxd *)skb->data; if (seq != rxd->seq) return -EAGAIN; if (cmd == MCU_CMD(PATCH_SEM_CONTROL)) { skb_pull(skb, sizeof(*rxd) - 4); ret = *skb->data; } else if ((rxd->option & MCU_UNI_CMD_EVENT) && rxd->eid == MCU_UNI_EVENT_RESULT) { skb_pull(skb, sizeof(*rxd)); event = (struct mt7996_mcu_uni_event *)skb->data; ret = le32_to_cpu(event->status); /* skip invalid event */ if (mcu_cmd != event->cid) ret = -EAGAIN; } else { skb_pull(skb, sizeof(struct mt7996_mcu_rxd)); } return ret; } static int mt7996_mcu_send_message(struct mt76_dev *mdev, struct sk_buff *skb, int cmd, int *wait_seq) { struct mt7996_dev *dev = container_of(mdev, struct mt7996_dev, mt76); int txd_len, mcu_cmd = FIELD_GET(__MCU_CMD_FIELD_ID, cmd); struct mt76_connac2_mcu_uni_txd *uni_txd; struct mt76_connac2_mcu_txd *mcu_txd; enum mt76_mcuq_id qid; __le32 *txd; u32 val; u8 seq; mdev->mcu.timeout = 20 * HZ; seq = ++dev->mt76.mcu.msg_seq & 0xf; if (!seq) seq = ++dev->mt76.mcu.msg_seq & 0xf; if (cmd == MCU_CMD(FW_SCATTER)) { qid = MT_MCUQ_FWDL; goto exit; } txd_len = cmd & __MCU_CMD_FIELD_UNI ? sizeof(*uni_txd) : sizeof(*mcu_txd); txd = (__le32 *)skb_push(skb, txd_len); if (test_bit(MT76_STATE_MCU_RUNNING, &dev->mphy.state) && mt7996_has_wa(dev)) qid = MT_MCUQ_WA; else qid = MT_MCUQ_WM; val = FIELD_PREP(MT_TXD0_TX_BYTES, skb->len) | FIELD_PREP(MT_TXD0_PKT_FMT, MT_TX_TYPE_CMD) | FIELD_PREP(MT_TXD0_Q_IDX, MT_TX_MCU_PORT_RX_Q0); txd[0] = cpu_to_le32(val); val = FIELD_PREP(MT_TXD1_HDR_FORMAT, MT_HDR_FORMAT_CMD); txd[1] = cpu_to_le32(val); if (cmd & __MCU_CMD_FIELD_UNI) { uni_txd = (struct mt76_connac2_mcu_uni_txd *)txd; uni_txd->len = cpu_to_le16(skb->len - sizeof(uni_txd->txd)); uni_txd->cid = cpu_to_le16(mcu_cmd); uni_txd->s2d_index = MCU_S2D_H2CN; uni_txd->pkt_type = MCU_PKT_ID; uni_txd->seq = seq; if (cmd & __MCU_CMD_FIELD_QUERY) uni_txd->option = MCU_CMD_UNI_QUERY_ACK; else uni_txd->option = MCU_CMD_UNI_EXT_ACK; if ((cmd & __MCU_CMD_FIELD_WA) && (cmd & __MCU_CMD_FIELD_WM)) uni_txd->s2d_index = MCU_S2D_H2CN; else if (cmd & __MCU_CMD_FIELD_WA) uni_txd->s2d_index = MCU_S2D_H2C; else if (cmd & __MCU_CMD_FIELD_WM) uni_txd->s2d_index = MCU_S2D_H2N; goto exit; } mcu_txd = (struct mt76_connac2_mcu_txd *)txd; mcu_txd->len = cpu_to_le16(skb->len - sizeof(mcu_txd->txd)); mcu_txd->pq_id = cpu_to_le16(MCU_PQ_ID(MT_TX_PORT_IDX_MCU, MT_TX_MCU_PORT_RX_Q0)); mcu_txd->pkt_type = MCU_PKT_ID; mcu_txd->seq = seq; mcu_txd->cid = FIELD_GET(__MCU_CMD_FIELD_ID, cmd); mcu_txd->set_query = MCU_Q_NA; mcu_txd->ext_cid = FIELD_GET(__MCU_CMD_FIELD_EXT_ID, cmd); if (mcu_txd->ext_cid) { mcu_txd->ext_cid_ack = 1; if (cmd & __MCU_CMD_FIELD_QUERY) mcu_txd->set_query = MCU_Q_QUERY; else mcu_txd->set_query = MCU_Q_SET; } if (cmd & __MCU_CMD_FIELD_WA) mcu_txd->s2d_index = MCU_S2D_H2C; else mcu_txd->s2d_index = MCU_S2D_H2N; exit: if (wait_seq) *wait_seq = seq; return mt76_tx_queue_skb_raw(dev, mdev->q_mcu[qid], skb, 0); } int mt7996_mcu_wa_cmd(struct mt7996_dev *dev, int cmd, u32 a1, u32 a2, u32 a3) { struct { u8 _rsv[4]; __le16 tag; __le16 len; __le32 args[3]; } __packed req = { .args = { cpu_to_le32(a1), cpu_to_le32(a2), cpu_to_le32(a3), }, }; if (mt7996_has_wa(dev)) return mt76_mcu_send_msg(&dev->mt76, cmd, &req.args, sizeof(req.args), false); req.tag = cpu_to_le16(cmd == MCU_WA_PARAM_CMD(QUERY) ? UNI_CMD_SDO_QUERY : UNI_CMD_SDO_SET); req.len = cpu_to_le16(sizeof(req) - 4); return mt76_mcu_send_msg(&dev->mt76, MCU_WA_UNI_CMD(SDO), &req, sizeof(req), false); } static void mt7996_mcu_csa_finish(void *priv, u8 *mac, struct ieee80211_vif *vif) { if (!vif->bss_conf.csa_active || vif->type == NL80211_IFTYPE_STATION) return; ieee80211_csa_finish(vif, 0); } static void mt7996_mcu_rx_radar_detected(struct mt7996_dev *dev, struct sk_buff *skb) { struct mt76_phy *mphy = &dev->mt76.phy; struct mt7996_mcu_rdd_report *r; r = (struct mt7996_mcu_rdd_report *)skb->data; switch (r->rdd_idx) { case MT_RDD_IDX_BAND2: mphy = dev->mt76.phys[MT_BAND2]; break; case MT_RDD_IDX_BAND1: mphy = dev->mt76.phys[MT_BAND1]; break; case MT_RDD_IDX_BACKGROUND: if (!dev->rdd2_phy) return; mphy = dev->rdd2_phy->mt76; break; default: dev_err(dev->mt76.dev, "Unknown RDD idx %d\n", r->rdd_idx); return; } if (!mphy) return; if (r->rdd_idx == MT_RDD_IDX_BACKGROUND) cfg80211_background_radar_event(mphy->hw->wiphy, &dev->rdd2_chandef, GFP_ATOMIC); else ieee80211_radar_detected(mphy->hw, NULL); dev->hw_pattern++; } static void mt7996_mcu_rx_log_message(struct mt7996_dev *dev, struct sk_buff *skb) { #define UNI_EVENT_FW_LOG_FORMAT 0 struct mt7996_mcu_rxd *rxd = (struct mt7996_mcu_rxd *)skb->data; const char *data = (char *)&rxd[1] + 4, *type; #if defined(__linux__) struct tlv *tlv = (struct tlv *)data; #elif defined(__FreeBSD__) const struct tlv *tlv = (const struct tlv *)data; #endif int len; if (!(rxd->option & MCU_UNI_CMD_EVENT)) { len = skb->len - sizeof(*rxd); data = (char *)&rxd[1]; goto out; } if (le16_to_cpu(tlv->tag) != UNI_EVENT_FW_LOG_FORMAT) return; data += sizeof(*tlv) + 4; len = le16_to_cpu(tlv->len) - sizeof(*tlv) - 4; out: switch (rxd->s2d_index) { case 0: #if defined(CONFIG_MT7996_DEBUGFS) if (mt7996_debugfs_rx_log(dev, data, len)) return; #endif type = "WM"; break; case 2: type = "WA"; break; default: type = "unknown"; break; } wiphy_info(mt76_hw(dev)->wiphy, "%s: %.*s", type, len, data); } static void mt7996_mcu_cca_finish(void *priv, u8 *mac, struct ieee80211_vif *vif) { if (!vif->bss_conf.color_change_active || vif->type == NL80211_IFTYPE_STATION) return; ieee80211_color_change_finish(vif, 0); } static void mt7996_mcu_ie_countdown(struct mt7996_dev *dev, struct sk_buff *skb) { #define UNI_EVENT_IE_COUNTDOWN_CSA 0 #define UNI_EVENT_IE_COUNTDOWN_BCC 1 struct header { u8 band; u8 rsv[3]; }; struct mt76_phy *mphy = &dev->mt76.phy; struct mt7996_mcu_rxd *rxd = (struct mt7996_mcu_rxd *)skb->data; const char *data = (char *)&rxd[1], *tail; #if defined(__linux__) struct header *hdr = (struct header *)data; struct tlv *tlv = (struct tlv *)(data + 4); #elif defined(__FreeBSD__) const struct header *hdr = (const struct header *)data; const struct tlv *tlv = (const struct tlv *)(data + 4); #endif if (hdr->band >= ARRAY_SIZE(dev->mt76.phys)) return; if (hdr->band && dev->mt76.phys[hdr->band]) mphy = dev->mt76.phys[hdr->band]; tail = skb->data + skb->len; data += sizeof(struct header); while (data + sizeof(struct tlv) < tail && le16_to_cpu(tlv->len)) { switch (le16_to_cpu(tlv->tag)) { case UNI_EVENT_IE_COUNTDOWN_CSA: ieee80211_iterate_active_interfaces_atomic(mphy->hw, IEEE80211_IFACE_ITER_RESUME_ALL, mt7996_mcu_csa_finish, mphy->hw); break; case UNI_EVENT_IE_COUNTDOWN_BCC: ieee80211_iterate_active_interfaces_atomic(mphy->hw, IEEE80211_IFACE_ITER_RESUME_ALL, mt7996_mcu_cca_finish, mphy->hw); break; } data += le16_to_cpu(tlv->len); #if defined(__linux__) tlv = (struct tlv *)data; #elif defined(__FreeBSD__) tlv = (const struct tlv *)data; #endif } } static int mt7996_mcu_update_tx_gi(struct rate_info *rate, struct all_sta_trx_rate *mcu_rate) { switch (mcu_rate->tx_mode) { case MT_PHY_TYPE_CCK: case MT_PHY_TYPE_OFDM: break; case MT_PHY_TYPE_HT: case MT_PHY_TYPE_HT_GF: case MT_PHY_TYPE_VHT: if (mcu_rate->tx_gi) rate->flags |= RATE_INFO_FLAGS_SHORT_GI; else rate->flags &= ~RATE_INFO_FLAGS_SHORT_GI; break; case MT_PHY_TYPE_HE_SU: case MT_PHY_TYPE_HE_EXT_SU: case MT_PHY_TYPE_HE_TB: case MT_PHY_TYPE_HE_MU: if (mcu_rate->tx_gi > NL80211_RATE_INFO_HE_GI_3_2) return -EINVAL; rate->he_gi = mcu_rate->tx_gi; break; case MT_PHY_TYPE_EHT_SU: case MT_PHY_TYPE_EHT_TRIG: case MT_PHY_TYPE_EHT_MU: if (mcu_rate->tx_gi > NL80211_RATE_INFO_EHT_GI_3_2) return -EINVAL; rate->eht_gi = mcu_rate->tx_gi; break; default: return -EINVAL; } return 0; } static void mt7996_mcu_rx_all_sta_info_event(struct mt7996_dev *dev, struct sk_buff *skb) { struct mt7996_mcu_all_sta_info_event *res; u16 i; skb_pull(skb, sizeof(struct mt7996_mcu_rxd)); res = (struct mt7996_mcu_all_sta_info_event *)skb->data; for (i = 0; i < le16_to_cpu(res->sta_num); i++) { u8 ac; u16 wlan_idx; struct mt76_wcid *wcid; switch (le16_to_cpu(res->tag)) { case UNI_ALL_STA_TXRX_RATE: wlan_idx = le16_to_cpu(res->rate[i].wlan_idx); wcid = mt76_wcid_ptr(dev, wlan_idx); if (!wcid) break; if (mt7996_mcu_update_tx_gi(&wcid->rate, &res->rate[i])) dev_err(dev->mt76.dev, "Failed to update TX GI\n"); break; case UNI_ALL_STA_TXRX_ADM_STAT: wlan_idx = le16_to_cpu(res->adm_stat[i].wlan_idx); wcid = mt76_wcid_ptr(dev, wlan_idx); if (!wcid) break; for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) { wcid->stats.tx_bytes += le32_to_cpu(res->adm_stat[i].tx_bytes[ac]); wcid->stats.rx_bytes += le32_to_cpu(res->adm_stat[i].rx_bytes[ac]); } break; case UNI_ALL_STA_TXRX_MSDU_COUNT: wlan_idx = le16_to_cpu(res->msdu_cnt[i].wlan_idx); wcid = mt76_wcid_ptr(dev, wlan_idx); if (!wcid) break; wcid->stats.tx_packets += le32_to_cpu(res->msdu_cnt[i].tx_msdu_cnt); wcid->stats.rx_packets += le32_to_cpu(res->msdu_cnt[i].rx_msdu_cnt); break; default: break; } } } static void mt7996_mcu_rx_thermal_notify(struct mt7996_dev *dev, struct sk_buff *skb) { #define THERMAL_NOTIFY_TAG 0x4 #define THERMAL_NOTIFY 0x2 struct mt76_phy *mphy = &dev->mt76.phy; struct mt7996_mcu_thermal_notify *n; struct mt7996_phy *phy; n = (struct mt7996_mcu_thermal_notify *)skb->data; if (le16_to_cpu(n->tag) != THERMAL_NOTIFY_TAG) return; if (n->event_id != THERMAL_NOTIFY) return; if (n->band_idx > MT_BAND2) return; mphy = dev->mt76.phys[n->band_idx]; if (!mphy) return; phy = (struct mt7996_phy *)mphy->priv; phy->throttle_state = n->duty_percent; } static void mt7996_mcu_rx_ext_event(struct mt7996_dev *dev, struct sk_buff *skb) { struct mt7996_mcu_rxd *rxd = (struct mt7996_mcu_rxd *)skb->data; switch (rxd->ext_eid) { case MCU_EXT_EVENT_FW_LOG_2_HOST: mt7996_mcu_rx_log_message(dev, skb); break; default: break; } } static void mt7996_mcu_rx_unsolicited_event(struct mt7996_dev *dev, struct sk_buff *skb) { struct mt7996_mcu_rxd *rxd = (struct mt7996_mcu_rxd *)skb->data; switch (rxd->eid) { case MCU_EVENT_EXT: mt7996_mcu_rx_ext_event(dev, skb); break; case MCU_UNI_EVENT_THERMAL: mt7996_mcu_rx_thermal_notify(dev, skb); break; default: break; } dev_kfree_skb(skb); } static void mt7996_mcu_wed_rro_event(struct mt7996_dev *dev, struct sk_buff *skb) { struct mt7996_mcu_wed_rro_event *event = (void *)skb->data; if (!dev->has_rro) return; skb_pull(skb, sizeof(struct mt7996_mcu_rxd) + 4); switch (le16_to_cpu(event->tag)) { case UNI_WED_RRO_BA_SESSION_STATUS: { struct mt7996_mcu_wed_rro_ba_event *e; while (skb->len >= sizeof(*e)) { struct mt76_rx_tid *tid; struct mt76_wcid *wcid; u16 idx; e = (void *)skb->data; idx = le16_to_cpu(e->wlan_id); wcid = mt76_wcid_ptr(dev, idx); if (!wcid || !wcid->sta) break; if (e->tid >= ARRAY_SIZE(wcid->aggr)) break; tid = rcu_dereference(wcid->aggr[e->tid]); if (!tid) break; tid->id = le16_to_cpu(e->id); skb_pull(skb, sizeof(*e)); } break; } case UNI_WED_RRO_BA_SESSION_DELETE: { struct mt7996_mcu_wed_rro_ba_delete_event *e; while (skb->len >= sizeof(*e)) { struct mt7996_wed_rro_session_id *session; e = (void *)skb->data; session = kzalloc(sizeof(*session), GFP_ATOMIC); if (!session) break; session->id = le16_to_cpu(e->session_id); spin_lock_bh(&dev->wed_rro.lock); list_add_tail(&session->list, &dev->wed_rro.poll_list); spin_unlock_bh(&dev->wed_rro.lock); ieee80211_queue_work(mt76_hw(dev), &dev->wed_rro.work); skb_pull(skb, sizeof(*e)); } break; } default: break; } } static void mt7996_mcu_uni_rx_unsolicited_event(struct mt7996_dev *dev, struct sk_buff *skb) { struct mt7996_mcu_rxd *rxd = (struct mt7996_mcu_rxd *)skb->data; switch (rxd->eid) { case MCU_UNI_EVENT_FW_LOG_2_HOST: mt7996_mcu_rx_log_message(dev, skb); break; case MCU_UNI_EVENT_IE_COUNTDOWN: mt7996_mcu_ie_countdown(dev, skb); break; case MCU_UNI_EVENT_RDD_REPORT: mt7996_mcu_rx_radar_detected(dev, skb); break; case MCU_UNI_EVENT_ALL_STA_INFO: mt7996_mcu_rx_all_sta_info_event(dev, skb); break; case MCU_UNI_EVENT_WED_RRO: mt7996_mcu_wed_rro_event(dev, skb); break; default: break; } dev_kfree_skb(skb); } void mt7996_mcu_rx_event(struct mt7996_dev *dev, struct sk_buff *skb) { struct mt7996_mcu_rxd *rxd = (struct mt7996_mcu_rxd *)skb->data; if (rxd->option & MCU_UNI_CMD_UNSOLICITED_EVENT) { mt7996_mcu_uni_rx_unsolicited_event(dev, skb); return; } /* WA still uses legacy event*/ if (rxd->ext_eid == MCU_EXT_EVENT_FW_LOG_2_HOST || !rxd->seq) mt7996_mcu_rx_unsolicited_event(dev, skb); else mt76_mcu_rx_event(&dev->mt76, skb); } static struct tlv * mt7996_mcu_add_uni_tlv(struct sk_buff *skb, u16 tag, u16 len) { struct tlv *ptlv = skb_put_zero(skb, len); ptlv->tag = cpu_to_le16(tag); ptlv->len = cpu_to_le16(len); return ptlv; } static void mt7996_mcu_bss_rfch_tlv(struct sk_buff *skb, struct mt7996_phy *phy) { static const u8 rlm_ch_band[] = { [NL80211_BAND_2GHZ] = 1, [NL80211_BAND_5GHZ] = 2, [NL80211_BAND_6GHZ] = 3, }; struct cfg80211_chan_def *chandef = &phy->mt76->chandef; struct bss_rlm_tlv *ch; struct tlv *tlv; int freq1 = chandef->center_freq1; tlv = mt7996_mcu_add_uni_tlv(skb, UNI_BSS_INFO_RLM, sizeof(*ch)); ch = (struct bss_rlm_tlv *)tlv; ch->control_channel = chandef->chan->hw_value; ch->center_chan = ieee80211_frequency_to_channel(freq1); ch->bw = mt76_connac_chan_bw(chandef); ch->tx_streams = hweight8(phy->mt76->antenna_mask); ch->rx_streams = hweight8(phy->mt76->antenna_mask); ch->band = rlm_ch_band[chandef->chan->band]; if (chandef->width == NL80211_CHAN_WIDTH_80P80) { int freq2 = chandef->center_freq2; ch->center_chan2 = ieee80211_frequency_to_channel(freq2); } } static void mt7996_mcu_bss_ra_tlv(struct sk_buff *skb, struct mt7996_phy *phy) { struct bss_ra_tlv *ra; struct tlv *tlv; tlv = mt7996_mcu_add_uni_tlv(skb, UNI_BSS_INFO_RA, sizeof(*ra)); ra = (struct bss_ra_tlv *)tlv; ra->short_preamble = true; } static void mt7996_mcu_bss_he_tlv(struct sk_buff *skb, struct ieee80211_vif *vif, struct ieee80211_bss_conf *link_conf, struct mt7996_phy *phy) { #define DEFAULT_HE_PE_DURATION 4 #define DEFAULT_HE_DURATION_RTS_THRES 1023 const struct ieee80211_sta_he_cap *cap; struct bss_info_uni_he *he; struct tlv *tlv; cap = mt76_connac_get_he_phy_cap(phy->mt76, vif); tlv = mt7996_mcu_add_uni_tlv(skb, UNI_BSS_INFO_HE_BASIC, sizeof(*he)); he = (struct bss_info_uni_he *)tlv; he->he_pe_duration = link_conf->htc_trig_based_pkt_ext; if (!he->he_pe_duration) he->he_pe_duration = DEFAULT_HE_PE_DURATION; he->he_rts_thres = cpu_to_le16(link_conf->frame_time_rts_th); if (!he->he_rts_thres) he->he_rts_thres = cpu_to_le16(DEFAULT_HE_DURATION_RTS_THRES); he->max_nss_mcs[CMD_HE_MCS_BW80] = cap->he_mcs_nss_supp.tx_mcs_80; he->max_nss_mcs[CMD_HE_MCS_BW160] = cap->he_mcs_nss_supp.tx_mcs_160; he->max_nss_mcs[CMD_HE_MCS_BW8080] = cap->he_mcs_nss_supp.tx_mcs_80p80; } static void mt7996_mcu_bss_mbssid_tlv(struct sk_buff *skb, struct ieee80211_bss_conf *link_conf, bool enable) { struct bss_info_uni_mbssid *mbssid; struct tlv *tlv; if (!link_conf->bssid_indicator && enable) return; tlv = mt7996_mcu_add_uni_tlv(skb, UNI_BSS_INFO_11V_MBSSID, sizeof(*mbssid)); mbssid = (struct bss_info_uni_mbssid *)tlv; if (enable) { mbssid->max_indicator = link_conf->bssid_indicator; mbssid->mbss_idx = link_conf->bssid_index; mbssid->tx_bss_omac_idx = 0; } } static void mt7996_mcu_bss_bmc_tlv(struct sk_buff *skb, struct mt76_vif_link *mlink, struct mt7996_phy *phy) { struct bss_rate_tlv *bmc; struct cfg80211_chan_def *chandef = &phy->mt76->chandef; enum nl80211_band band = chandef->chan->band; struct tlv *tlv; u8 idx = mlink->mcast_rates_idx ? mlink->mcast_rates_idx : mlink->basic_rates_idx; tlv = mt7996_mcu_add_uni_tlv(skb, UNI_BSS_INFO_RATE, sizeof(*bmc)); bmc = (struct bss_rate_tlv *)tlv; bmc->short_preamble = (band == NL80211_BAND_2GHZ); bmc->bc_fixed_rate = idx; bmc->mc_fixed_rate = idx; } static void mt7996_mcu_bss_txcmd_tlv(struct sk_buff *skb, bool en) { struct bss_txcmd_tlv *txcmd; struct tlv *tlv; tlv = mt7996_mcu_add_uni_tlv(skb, UNI_BSS_INFO_TXCMD, sizeof(*txcmd)); txcmd = (struct bss_txcmd_tlv *)tlv; txcmd->txcmd_mode = en; } static void mt7996_mcu_bss_mld_tlv(struct sk_buff *skb, struct mt76_vif_link *mlink) { struct bss_mld_tlv *mld; struct tlv *tlv; tlv = mt7996_mcu_add_uni_tlv(skb, UNI_BSS_INFO_MLD, sizeof(*mld)); mld = (struct bss_mld_tlv *)tlv; mld->group_mld_id = 0xff; mld->own_mld_id = mlink->idx; mld->remap_idx = 0xff; } static void mt7996_mcu_bss_sec_tlv(struct sk_buff *skb, struct mt76_vif_link *mlink) { struct bss_sec_tlv *sec; struct tlv *tlv; tlv = mt7996_mcu_add_uni_tlv(skb, UNI_BSS_INFO_SEC, sizeof(*sec)); sec = (struct bss_sec_tlv *)tlv; sec->cipher = mlink->cipher; } static int mt7996_mcu_muar_config(struct mt7996_dev *dev, struct mt76_vif_link *mlink, const u8 *addr, bool bssid, bool enable) { #define UNI_MUAR_ENTRY 2 u32 idx = mlink->omac_idx - REPEATER_BSSID_START; struct { struct { u8 band; u8 __rsv[3]; } hdr; __le16 tag; __le16 len; bool smesh; u8 bssid; u8 index; u8 entry_add; u8 addr[ETH_ALEN]; u8 __rsv[2]; } __packed req = { .hdr.band = mlink->band_idx, .tag = cpu_to_le16(UNI_MUAR_ENTRY), .len = cpu_to_le16(sizeof(req) - sizeof(req.hdr)), .smesh = false, .index = idx * 2 + bssid, .entry_add = true, }; if (enable) memcpy(req.addr, addr, ETH_ALEN); return mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(REPT_MUAR), &req, sizeof(req), true); } static void mt7996_mcu_bss_ifs_timing_tlv(struct sk_buff *skb, struct mt7996_phy *phy) { struct bss_ifs_time_tlv *ifs_time; struct tlv *tlv; bool is_2ghz = phy->mt76->chandef.chan->band == NL80211_BAND_2GHZ; tlv = mt7996_mcu_add_uni_tlv(skb, UNI_BSS_INFO_IFS_TIME, sizeof(*ifs_time)); ifs_time = (struct bss_ifs_time_tlv *)tlv; ifs_time->slot_valid = true; ifs_time->sifs_valid = true; ifs_time->rifs_valid = true; ifs_time->eifs_valid = true; ifs_time->slot_time = cpu_to_le16(phy->slottime); ifs_time->sifs_time = cpu_to_le16(10); ifs_time->rifs_time = cpu_to_le16(2); ifs_time->eifs_time = cpu_to_le16(is_2ghz ? 78 : 84); if (is_2ghz) { ifs_time->eifs_cck_valid = true; ifs_time->eifs_cck_time = cpu_to_le16(314); } } static int mt7996_mcu_bss_basic_tlv(struct sk_buff *skb, struct ieee80211_vif *vif, struct ieee80211_bss_conf *link_conf, struct mt76_vif_link *mvif, struct mt76_phy *phy, u16 wlan_idx, bool enable) { struct cfg80211_chan_def *chandef = &phy->chandef; struct mt76_connac_bss_basic_tlv *bss; u32 type = CONNECTION_INFRA_AP; u16 sta_wlan_idx = wlan_idx; struct ieee80211_sta *sta; struct tlv *tlv; int idx; switch (vif->type) { case NL80211_IFTYPE_MESH_POINT: case NL80211_IFTYPE_AP: case NL80211_IFTYPE_MONITOR: break; case NL80211_IFTYPE_STATION: if (enable) { rcu_read_lock(); sta = ieee80211_find_sta(vif, vif->bss_conf.bssid); /* TODO: enable BSS_INFO_UAPSD & BSS_INFO_PM */ if (sta) { struct mt76_wcid *wcid; wcid = (struct mt76_wcid *)sta->drv_priv; sta_wlan_idx = wcid->idx; } rcu_read_unlock(); } type = CONNECTION_INFRA_STA; break; case NL80211_IFTYPE_ADHOC: type = CONNECTION_IBSS_ADHOC; break; default: WARN_ON(1); break; } tlv = mt7996_mcu_add_uni_tlv(skb, UNI_BSS_INFO_BASIC, sizeof(*bss)); bss = (struct mt76_connac_bss_basic_tlv *)tlv; bss->bcn_interval = cpu_to_le16(link_conf->beacon_int); bss->dtim_period = link_conf->dtim_period; bss->bmc_tx_wlan_idx = cpu_to_le16(wlan_idx); bss->sta_idx = cpu_to_le16(sta_wlan_idx); bss->conn_type = cpu_to_le32(type); bss->omac_idx = mvif->omac_idx; bss->band_idx = mvif->band_idx; bss->wmm_idx = mvif->wmm_idx; bss->conn_state = !enable; bss->active = enable; idx = mvif->omac_idx > EXT_BSSID_START ? HW_BSSID_0 : mvif->omac_idx; bss->hw_bss_idx = idx; if (vif->type == NL80211_IFTYPE_MONITOR) { memcpy(bss->bssid, phy->macaddr, ETH_ALEN); return 0; } memcpy(bss->bssid, link_conf->bssid, ETH_ALEN); bss->bcn_interval = cpu_to_le16(link_conf->beacon_int); bss->dtim_period = vif->bss_conf.dtim_period; bss->phymode = mt76_connac_get_phy_mode(phy, vif, chandef->chan->band, NULL); bss->phymode_ext = mt76_connac_get_phy_mode_ext(phy, &vif->bss_conf, chandef->chan->band); return 0; } static struct sk_buff * __mt7996_mcu_alloc_bss_req(struct mt76_dev *dev, struct mt76_vif_link *mvif, int len) { struct bss_req_hdr hdr = { .bss_idx = mvif->idx, }; struct sk_buff *skb; skb = mt76_mcu_msg_alloc(dev, NULL, len); if (!skb) return ERR_PTR(-ENOMEM); skb_put_data(skb, &hdr, sizeof(hdr)); return skb; } int mt7996_mcu_add_bss_info(struct mt7996_phy *phy, struct ieee80211_vif *vif, struct ieee80211_bss_conf *link_conf, struct mt76_vif_link *mlink, struct mt7996_sta_link *msta_link, int enable) { struct mt7996_dev *dev = phy->dev; struct sk_buff *skb; if (mlink->omac_idx >= REPEATER_BSSID_START) { mt7996_mcu_muar_config(dev, mlink, link_conf->addr, false, enable); mt7996_mcu_muar_config(dev, mlink, link_conf->bssid, true, enable); } skb = __mt7996_mcu_alloc_bss_req(&dev->mt76, mlink, MT7996_BSS_UPDATE_MAX_SIZE); if (IS_ERR(skb)) return PTR_ERR(skb); /* bss_basic must be first */ mt7996_mcu_bss_basic_tlv(skb, vif, link_conf, mlink, phy->mt76, msta_link->wcid.idx, enable); mt7996_mcu_bss_sec_tlv(skb, mlink); if (vif->type == NL80211_IFTYPE_MONITOR) goto out; if (enable) { mt7996_mcu_bss_rfch_tlv(skb, phy); mt7996_mcu_bss_bmc_tlv(skb, mlink, phy); mt7996_mcu_bss_ra_tlv(skb, phy); mt7996_mcu_bss_txcmd_tlv(skb, true); mt7996_mcu_bss_ifs_timing_tlv(skb, phy); if (vif->bss_conf.he_support) mt7996_mcu_bss_he_tlv(skb, vif, link_conf, phy); /* this tag is necessary no matter if the vif is MLD */ mt7996_mcu_bss_mld_tlv(skb, mlink); } mt7996_mcu_bss_mbssid_tlv(skb, link_conf, enable); out: return mt76_mcu_skb_send_msg(&dev->mt76, skb, MCU_WMWA_UNI_CMD(BSS_INFO_UPDATE), true); } int mt7996_mcu_set_timing(struct mt7996_phy *phy, struct ieee80211_vif *vif, struct ieee80211_bss_conf *link_conf) { struct mt7996_dev *dev = phy->dev; struct mt76_vif_link *mlink = mt76_vif_conf_link(&dev->mt76, vif, link_conf); struct sk_buff *skb; skb = __mt7996_mcu_alloc_bss_req(&dev->mt76, mlink, MT7996_BSS_UPDATE_MAX_SIZE); if (IS_ERR(skb)) return PTR_ERR(skb); mt7996_mcu_bss_ifs_timing_tlv(skb, phy); return mt76_mcu_skb_send_msg(&dev->mt76, skb, MCU_WMWA_UNI_CMD(BSS_INFO_UPDATE), true); } static int mt7996_mcu_sta_ba(struct mt7996_dev *dev, struct mt76_vif_link *mvif, struct ieee80211_ampdu_params *params, bool enable, bool tx) { struct mt76_wcid *wcid = (struct mt76_wcid *)params->sta->drv_priv; struct sta_rec_ba_uni *ba; struct sk_buff *skb; struct tlv *tlv; skb = __mt76_connac_mcu_alloc_sta_req(&dev->mt76, mvif, wcid, MT7996_STA_UPDATE_MAX_SIZE); if (IS_ERR(skb)) return PTR_ERR(skb); tlv = mt76_connac_mcu_add_tlv(skb, STA_REC_BA, sizeof(*ba)); ba = (struct sta_rec_ba_uni *)tlv; ba->ba_type = tx ? MT_BA_TYPE_ORIGINATOR : MT_BA_TYPE_RECIPIENT; ba->winsize = cpu_to_le16(params->buf_size); ba->ssn = cpu_to_le16(params->ssn); ba->ba_en = enable << params->tid; ba->amsdu = params->amsdu; ba->tid = params->tid; ba->ba_rdd_rro = !tx && enable && dev->has_rro; return mt76_mcu_skb_send_msg(&dev->mt76, skb, MCU_WMWA_UNI_CMD(STA_REC_UPDATE), true); } /** starec & wtbl **/ int mt7996_mcu_add_tx_ba(struct mt7996_dev *dev, struct ieee80211_ampdu_params *params, struct mt7996_vif_link *link, struct mt7996_sta_link *msta_link, bool enable) { if (enable && !params->amsdu) msta_link->wcid.amsdu = false; return mt7996_mcu_sta_ba(dev, &link->mt76, params, enable, true); } int mt7996_mcu_add_rx_ba(struct mt7996_dev *dev, struct ieee80211_ampdu_params *params, struct mt7996_vif_link *link, bool enable) { return mt7996_mcu_sta_ba(dev, &link->mt76, params, enable, false); } static void mt7996_mcu_sta_he_tlv(struct sk_buff *skb, struct ieee80211_link_sta *link_sta, struct mt7996_vif_link *link) { struct ieee80211_he_cap_elem *elem = &link_sta->he_cap.he_cap_elem; struct ieee80211_he_mcs_nss_supp mcs_map; struct sta_rec_he_v2 *he; struct tlv *tlv; int i = 0; if (!link_sta->he_cap.has_he) return; tlv = mt76_connac_mcu_add_tlv(skb, STA_REC_HE_V2, sizeof(*he)); he = (struct sta_rec_he_v2 *)tlv; for (i = 0; i < 11; i++) { if (i < 6) he->he_mac_cap[i] = elem->mac_cap_info[i]; he->he_phy_cap[i] = elem->phy_cap_info[i]; } mcs_map = link_sta->he_cap.he_mcs_nss_supp; switch (link_sta->bandwidth) { case IEEE80211_STA_RX_BW_160: if (elem->phy_cap_info[0] & IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_80PLUS80_MHZ_IN_5G) mt7996_mcu_set_sta_he_mcs(link_sta, link, &he->max_nss_mcs[CMD_HE_MCS_BW8080], le16_to_cpu(mcs_map.rx_mcs_80p80)); mt7996_mcu_set_sta_he_mcs(link_sta, link, &he->max_nss_mcs[CMD_HE_MCS_BW160], le16_to_cpu(mcs_map.rx_mcs_160)); fallthrough; default: mt7996_mcu_set_sta_he_mcs(link_sta, link, &he->max_nss_mcs[CMD_HE_MCS_BW80], le16_to_cpu(mcs_map.rx_mcs_80)); break; } he->pkt_ext = 2; } static void mt7996_mcu_sta_he_6g_tlv(struct sk_buff *skb, struct ieee80211_link_sta *link_sta) { struct sta_rec_he_6g_capa *he_6g; struct tlv *tlv; if (!link_sta->he_6ghz_capa.capa) return; tlv = mt76_connac_mcu_add_tlv(skb, STA_REC_HE_6G, sizeof(*he_6g)); he_6g = (struct sta_rec_he_6g_capa *)tlv; he_6g->capa = link_sta->he_6ghz_capa.capa; } static void mt7996_mcu_sta_eht_tlv(struct sk_buff *skb, struct ieee80211_link_sta *link_sta) { struct mt7996_sta *msta = (struct mt7996_sta *)link_sta->sta->drv_priv; struct ieee80211_vif *vif = container_of((void *)msta->vif, struct ieee80211_vif, drv_priv); struct ieee80211_eht_mcs_nss_supp *mcs_map; struct ieee80211_eht_cap_elem_fixed *elem; struct sta_rec_eht *eht; struct tlv *tlv; if (!link_sta->eht_cap.has_eht) return; mcs_map = &link_sta->eht_cap.eht_mcs_nss_supp; elem = &link_sta->eht_cap.eht_cap_elem; tlv = mt76_connac_mcu_add_tlv(skb, STA_REC_EHT, sizeof(*eht)); eht = (struct sta_rec_eht *)tlv; eht->tid_bitmap = 0xff; eht->mac_cap = cpu_to_le16(*(u16 *)elem->mac_cap_info); eht->phy_cap = cpu_to_le64(*(u64 *)elem->phy_cap_info); eht->phy_cap_ext = cpu_to_le64(elem->phy_cap_info[8]); if (vif->type != NL80211_IFTYPE_STATION && (link_sta->he_cap.he_cap_elem.phy_cap_info[0] & (IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_40MHZ_IN_2G | IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_40MHZ_80MHZ_IN_5G | IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_160MHZ_IN_5G | IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_80PLUS80_MHZ_IN_5G)) == 0) { memcpy(eht->mcs_map_bw20, &mcs_map->only_20mhz, sizeof(eht->mcs_map_bw20)); return; } memcpy(eht->mcs_map_bw80, &mcs_map->bw._80, sizeof(eht->mcs_map_bw80)); memcpy(eht->mcs_map_bw160, &mcs_map->bw._160, sizeof(eht->mcs_map_bw160)); memcpy(eht->mcs_map_bw320, &mcs_map->bw._320, sizeof(eht->mcs_map_bw320)); } static void mt7996_mcu_sta_ht_tlv(struct sk_buff *skb, struct ieee80211_link_sta *link_sta) { struct sta_rec_ht_uni *ht; struct tlv *tlv; if (!link_sta->ht_cap.ht_supported) return; tlv = mt76_connac_mcu_add_tlv(skb, STA_REC_HT, sizeof(*ht)); ht = (struct sta_rec_ht_uni *)tlv; ht->ht_cap = cpu_to_le16(link_sta->ht_cap.cap); ht->ampdu_param = u8_encode_bits(link_sta->ht_cap.ampdu_factor, IEEE80211_HT_AMPDU_PARM_FACTOR) | u8_encode_bits(link_sta->ht_cap.ampdu_density, IEEE80211_HT_AMPDU_PARM_DENSITY); } static void mt7996_mcu_sta_vht_tlv(struct sk_buff *skb, struct ieee80211_link_sta *link_sta) { struct sta_rec_vht *vht; struct tlv *tlv; /* For 6G band, this tlv is necessary to let hw work normally */ if (!link_sta->he_6ghz_capa.capa && !link_sta->vht_cap.vht_supported) return; tlv = mt76_connac_mcu_add_tlv(skb, STA_REC_VHT, sizeof(*vht)); vht = (struct sta_rec_vht *)tlv; vht->vht_cap = cpu_to_le32(link_sta->vht_cap.cap); vht->vht_rx_mcs_map = link_sta->vht_cap.vht_mcs.rx_mcs_map; vht->vht_tx_mcs_map = link_sta->vht_cap.vht_mcs.tx_mcs_map; } static void mt7996_mcu_sta_amsdu_tlv(struct mt7996_dev *dev, struct sk_buff *skb, struct ieee80211_vif *vif, struct ieee80211_link_sta *link_sta, struct mt7996_sta_link *msta_link) { struct sta_rec_amsdu *amsdu; struct tlv *tlv; if (vif->type != NL80211_IFTYPE_STATION && vif->type != NL80211_IFTYPE_MESH_POINT && vif->type != NL80211_IFTYPE_AP) return; if (!link_sta->agg.max_amsdu_len) return; tlv = mt76_connac_mcu_add_tlv(skb, STA_REC_HW_AMSDU, sizeof(*amsdu)); amsdu = (struct sta_rec_amsdu *)tlv; amsdu->max_amsdu_num = 8; amsdu->amsdu_en = true; msta_link->wcid.amsdu = true; switch (link_sta->agg.max_amsdu_len) { case IEEE80211_MAX_MPDU_LEN_VHT_11454: amsdu->max_mpdu_size = IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_11454; return; case IEEE80211_MAX_MPDU_LEN_HT_7935: case IEEE80211_MAX_MPDU_LEN_VHT_7991: amsdu->max_mpdu_size = IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_7991; return; default: amsdu->max_mpdu_size = IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_3895; return; } } static void mt7996_mcu_sta_muru_tlv(struct mt7996_dev *dev, struct sk_buff *skb, struct ieee80211_bss_conf *link_conf, struct ieee80211_link_sta *link_sta) { struct ieee80211_he_cap_elem *elem = &link_sta->he_cap.he_cap_elem; struct sta_rec_muru *muru; struct tlv *tlv; if (link_conf->vif->type != NL80211_IFTYPE_STATION && link_conf->vif->type != NL80211_IFTYPE_AP) return; tlv = mt76_connac_mcu_add_tlv(skb, STA_REC_MURU, sizeof(*muru)); muru = (struct sta_rec_muru *)tlv; muru->cfg.mimo_dl_en = link_conf->eht_mu_beamformer || link_conf->he_mu_beamformer || link_conf->vht_mu_beamformer || link_conf->vht_mu_beamformee; muru->cfg.ofdma_dl_en = true; if (link_sta->vht_cap.vht_supported) muru->mimo_dl.vht_mu_bfee = !!(link_sta->vht_cap.cap & IEEE80211_VHT_CAP_MU_BEAMFORMEE_CAPABLE); if (!link_sta->he_cap.has_he) return; muru->mimo_dl.partial_bw_dl_mimo = HE_PHY(CAP6_PARTIAL_BANDWIDTH_DL_MUMIMO, elem->phy_cap_info[6]); muru->mimo_ul.full_ul_mimo = HE_PHY(CAP2_UL_MU_FULL_MU_MIMO, elem->phy_cap_info[2]); muru->mimo_ul.partial_ul_mimo = HE_PHY(CAP2_UL_MU_PARTIAL_MU_MIMO, elem->phy_cap_info[2]); muru->ofdma_dl.punc_pream_rx = HE_PHY(CAP1_PREAMBLE_PUNC_RX_MASK, elem->phy_cap_info[1]); muru->ofdma_dl.he_20m_in_40m_2g = HE_PHY(CAP8_20MHZ_IN_40MHZ_HE_PPDU_IN_2G, elem->phy_cap_info[8]); muru->ofdma_dl.he_20m_in_160m = HE_PHY(CAP8_20MHZ_IN_160MHZ_HE_PPDU, elem->phy_cap_info[8]); muru->ofdma_dl.he_80m_in_160m = HE_PHY(CAP8_80MHZ_IN_160MHZ_HE_PPDU, elem->phy_cap_info[8]); muru->ofdma_ul.t_frame_dur = HE_MAC(CAP1_TF_MAC_PAD_DUR_MASK, elem->mac_cap_info[1]); muru->ofdma_ul.mu_cascading = HE_MAC(CAP2_MU_CASCADING, elem->mac_cap_info[2]); muru->ofdma_ul.uo_ra = HE_MAC(CAP3_OFDMA_RA, elem->mac_cap_info[3]); muru->ofdma_ul.rx_ctrl_frame_to_mbss = HE_MAC(CAP3_RX_CTRL_FRAME_TO_MULTIBSS, elem->mac_cap_info[3]); } static inline bool mt7996_is_ebf_supported(struct mt7996_phy *phy, struct ieee80211_bss_conf *link_conf, struct ieee80211_link_sta *link_sta, bool bfee) { int sts = hweight16(phy->mt76->chainmask); if (link_conf->vif->type != NL80211_IFTYPE_STATION && link_conf->vif->type != NL80211_IFTYPE_AP) return false; if (!bfee && sts < 2) return false; if (link_sta->eht_cap.has_eht) { struct ieee80211_sta_eht_cap *pc = &link_sta->eht_cap; struct ieee80211_eht_cap_elem_fixed *pe = &pc->eht_cap_elem; if (bfee) return link_conf->eht_su_beamformee && EHT_PHY(CAP0_SU_BEAMFORMER, pe->phy_cap_info[0]); else return link_conf->eht_su_beamformer && EHT_PHY(CAP0_SU_BEAMFORMEE, pe->phy_cap_info[0]); } if (link_sta->he_cap.has_he) { struct ieee80211_he_cap_elem *pe = &link_sta->he_cap.he_cap_elem; if (bfee) return link_conf->he_su_beamformee && HE_PHY(CAP3_SU_BEAMFORMER, pe->phy_cap_info[3]); else return link_conf->he_su_beamformer && HE_PHY(CAP4_SU_BEAMFORMEE, pe->phy_cap_info[4]); } if (link_sta->vht_cap.vht_supported) { u32 cap = link_sta->vht_cap.cap; if (bfee) return link_conf->vht_su_beamformee && (cap & IEEE80211_VHT_CAP_SU_BEAMFORMER_CAPABLE); else return link_conf->vht_su_beamformer && (cap & IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE); } return false; } static void mt7996_mcu_sta_sounding_rate(struct sta_rec_bf *bf, struct mt7996_phy *phy) { bf->sounding_phy = MT_PHY_TYPE_OFDM; bf->ndp_rate = 0; /* mcs0 */ if (is_mt7996(phy->mt76->dev)) bf->ndpa_rate = MT7996_CFEND_RATE_DEFAULT; /* ofdm 24m */ else bf->ndpa_rate = MT7992_CFEND_RATE_DEFAULT; /* ofdm 6m */ bf->rept_poll_rate = MT7996_CFEND_RATE_DEFAULT; /* ofdm 24m */ } static void mt7996_mcu_sta_bfer_ht(struct ieee80211_link_sta *link_sta, struct mt7996_phy *phy, struct sta_rec_bf *bf, bool explicit) { struct ieee80211_mcs_info *mcs = &link_sta->ht_cap.mcs; u8 n = 0; bf->tx_mode = MT_PHY_TYPE_HT; if ((mcs->tx_params & IEEE80211_HT_MCS_TX_RX_DIFF) && (mcs->tx_params & IEEE80211_HT_MCS_TX_DEFINED)) n = FIELD_GET(IEEE80211_HT_MCS_TX_MAX_STREAMS_MASK, mcs->tx_params); else if (mcs->rx_mask[3]) n = 3; else if (mcs->rx_mask[2]) n = 2; else if (mcs->rx_mask[1]) n = 1; bf->nrow = hweight8(phy->mt76->antenna_mask) - 1; bf->ncol = min_t(u8, bf->nrow, n); bf->ibf_ncol = explicit ? min_t(u8, MT7996_IBF_MAX_NC, bf->ncol) : min_t(u8, MT7996_IBF_MAX_NC, n); } static void mt7996_mcu_sta_bfer_vht(struct ieee80211_link_sta *link_sta, struct mt7996_phy *phy, struct sta_rec_bf *bf, bool explicit) { struct ieee80211_sta_vht_cap *pc = &link_sta->vht_cap; struct ieee80211_sta_vht_cap *vc = &phy->mt76->sband_5g.sband.vht_cap; u16 mcs_map = le16_to_cpu(pc->vht_mcs.rx_mcs_map); u8 nss_mcs = mt7996_mcu_get_sta_nss(mcs_map); u8 tx_ant = hweight8(phy->mt76->antenna_mask) - 1; bf->tx_mode = MT_PHY_TYPE_VHT; if (explicit) { u8 sts, snd_dim; mt7996_mcu_sta_sounding_rate(bf, phy); sts = FIELD_GET(IEEE80211_VHT_CAP_BEAMFORMEE_STS_MASK, pc->cap); snd_dim = FIELD_GET(IEEE80211_VHT_CAP_SOUNDING_DIMENSIONS_MASK, vc->cap); bf->nrow = min_t(u8, min_t(u8, snd_dim, sts), tx_ant); bf->ncol = min_t(u8, nss_mcs, bf->nrow); bf->ibf_ncol = min_t(u8, MT7996_IBF_MAX_NC, bf->ncol); if (link_sta->bandwidth == IEEE80211_STA_RX_BW_160) bf->nrow = 1; } else { bf->nrow = tx_ant; bf->ncol = min_t(u8, nss_mcs, bf->nrow); bf->ibf_ncol = min_t(u8, MT7996_IBF_MAX_NC, nss_mcs); if (link_sta->bandwidth == IEEE80211_STA_RX_BW_160) bf->ibf_nrow = 1; } } static void mt7996_mcu_sta_bfer_he(struct ieee80211_link_sta *link_sta, struct ieee80211_vif *vif, struct mt7996_phy *phy, struct sta_rec_bf *bf, bool explicit) { struct ieee80211_sta_he_cap *pc = &link_sta->he_cap; struct ieee80211_he_cap_elem *pe = &pc->he_cap_elem; const struct ieee80211_sta_he_cap *vc = mt76_connac_get_he_phy_cap(phy->mt76, vif); const struct ieee80211_he_cap_elem *ve = &vc->he_cap_elem; u16 mcs_map = le16_to_cpu(pc->he_mcs_nss_supp.rx_mcs_80); u8 nss_mcs = mt7996_mcu_get_sta_nss(mcs_map); u8 snd_dim, sts; if (!vc) return; bf->tx_mode = MT_PHY_TYPE_HE_SU; mt7996_mcu_sta_sounding_rate(bf, phy); bf->trigger_su = HE_PHY(CAP6_TRIG_SU_BEAMFORMING_FB, pe->phy_cap_info[6]); bf->trigger_mu = HE_PHY(CAP6_TRIG_MU_BEAMFORMING_PARTIAL_BW_FB, pe->phy_cap_info[6]); snd_dim = HE_PHY(CAP5_BEAMFORMEE_NUM_SND_DIM_UNDER_80MHZ_MASK, ve->phy_cap_info[5]); sts = HE_PHY(CAP4_BEAMFORMEE_MAX_STS_UNDER_80MHZ_MASK, pe->phy_cap_info[4]); bf->nrow = min_t(u8, snd_dim, sts); bf->ncol = min_t(u8, nss_mcs, bf->nrow); bf->ibf_ncol = explicit ? min_t(u8, MT7996_IBF_MAX_NC, bf->ncol) : min_t(u8, MT7996_IBF_MAX_NC, nss_mcs); if (link_sta->bandwidth != IEEE80211_STA_RX_BW_160) return; /* go over for 160MHz and 80p80 */ if (pe->phy_cap_info[0] & IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_160MHZ_IN_5G) { mcs_map = le16_to_cpu(pc->he_mcs_nss_supp.rx_mcs_160); nss_mcs = mt7996_mcu_get_sta_nss(mcs_map); bf->ncol_gt_bw80 = nss_mcs; } if (pe->phy_cap_info[0] & IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_80PLUS80_MHZ_IN_5G) { mcs_map = le16_to_cpu(pc->he_mcs_nss_supp.rx_mcs_80p80); nss_mcs = mt7996_mcu_get_sta_nss(mcs_map); if (bf->ncol_gt_bw80) bf->ncol_gt_bw80 = min_t(u8, bf->ncol_gt_bw80, nss_mcs); else bf->ncol_gt_bw80 = nss_mcs; } snd_dim = HE_PHY(CAP5_BEAMFORMEE_NUM_SND_DIM_ABOVE_80MHZ_MASK, ve->phy_cap_info[5]); sts = HE_PHY(CAP4_BEAMFORMEE_MAX_STS_ABOVE_80MHZ_MASK, pe->phy_cap_info[4]); bf->nrow_gt_bw80 = min_t(int, snd_dim, sts); } static void mt7996_mcu_sta_bfer_eht(struct ieee80211_link_sta *link_sta, struct ieee80211_vif *vif, struct mt7996_phy *phy, struct sta_rec_bf *bf, bool explicit) { struct ieee80211_sta_eht_cap *pc = &link_sta->eht_cap; struct ieee80211_eht_cap_elem_fixed *pe = &pc->eht_cap_elem; struct ieee80211_eht_mcs_nss_supp *eht_nss = &pc->eht_mcs_nss_supp; const struct ieee80211_sta_eht_cap *vc = mt76_connac_get_eht_phy_cap(phy->mt76, vif); const struct ieee80211_eht_cap_elem_fixed *ve = &vc->eht_cap_elem; u8 nss_mcs = u8_get_bits(eht_nss->bw._80.rx_tx_mcs9_max_nss, IEEE80211_EHT_MCS_NSS_RX) - 1; u8 snd_dim, sts; bf->tx_mode = MT_PHY_TYPE_EHT_MU; mt7996_mcu_sta_sounding_rate(bf, phy); bf->trigger_su = EHT_PHY(CAP3_TRIG_SU_BF_FDBK, pe->phy_cap_info[3]); bf->trigger_mu = EHT_PHY(CAP3_TRIG_MU_BF_PART_BW_FDBK, pe->phy_cap_info[3]); snd_dim = EHT_PHY(CAP2_SOUNDING_DIM_80MHZ_MASK, ve->phy_cap_info[2]); sts = EHT_PHY(CAP0_BEAMFORMEE_SS_80MHZ_MASK, pe->phy_cap_info[0]) + (EHT_PHY(CAP1_BEAMFORMEE_SS_80MHZ_MASK, pe->phy_cap_info[1]) << 1); bf->nrow = min_t(u8, snd_dim, sts); bf->ncol = min_t(u8, nss_mcs, bf->nrow); bf->ibf_ncol = explicit ? min_t(u8, MT7996_IBF_MAX_NC, bf->ncol) : min_t(u8, MT7996_IBF_MAX_NC, nss_mcs); if (link_sta->bandwidth < IEEE80211_STA_RX_BW_160) return; switch (link_sta->bandwidth) { case IEEE80211_STA_RX_BW_160: snd_dim = EHT_PHY(CAP2_SOUNDING_DIM_160MHZ_MASK, ve->phy_cap_info[2]); sts = EHT_PHY(CAP1_BEAMFORMEE_SS_160MHZ_MASK, pe->phy_cap_info[1]); nss_mcs = u8_get_bits(eht_nss->bw._160.rx_tx_mcs9_max_nss, IEEE80211_EHT_MCS_NSS_RX) - 1; bf->nrow_gt_bw80 = min_t(u8, snd_dim, sts); bf->ncol_gt_bw80 = nss_mcs; break; case IEEE80211_STA_RX_BW_320: snd_dim = EHT_PHY(CAP2_SOUNDING_DIM_320MHZ_MASK, ve->phy_cap_info[2]) + (EHT_PHY(CAP3_SOUNDING_DIM_320MHZ_MASK, ve->phy_cap_info[3]) << 1); sts = EHT_PHY(CAP1_BEAMFORMEE_SS_320MHZ_MASK, pe->phy_cap_info[1]); nss_mcs = u8_get_bits(eht_nss->bw._320.rx_tx_mcs9_max_nss, IEEE80211_EHT_MCS_NSS_RX) - 1; bf->nrow_gt_bw80 = min_t(u8, snd_dim, sts) << 4; bf->ncol_gt_bw80 = nss_mcs << 4; break; default: break; } } static void mt7996_mcu_sta_bfer_tlv(struct mt7996_dev *dev, struct sk_buff *skb, struct ieee80211_bss_conf *link_conf, struct ieee80211_link_sta *link_sta, struct mt7996_vif_link *link) { #define EBF_MODE BIT(0) #define IBF_MODE BIT(1) #define BF_MAT_ORDER 4 struct ieee80211_vif *vif = link_conf->vif; struct mt7996_phy *phy = link->phy; int tx_ant = hweight16(phy->mt76->chainmask) - 1; struct sta_rec_bf *bf; struct tlv *tlv; static const u8 matrix[BF_MAT_ORDER][BF_MAT_ORDER] = { {0, 0, 0, 0}, {1, 1, 0, 0}, /* 2x1, 2x2, 2x3, 2x4 */ {2, 4, 4, 0}, /* 3x1, 3x2, 3x3, 3x4 */ {3, 5, 6, 0} /* 4x1, 4x2, 4x3, 4x4 */ }; bool ebf; if (!(link_sta->ht_cap.ht_supported || link_sta->he_cap.has_he)) return; ebf = mt7996_is_ebf_supported(phy, link_conf, link_sta, false); if (!ebf && !dev->ibf) return; tlv = mt76_connac_mcu_add_tlv(skb, STA_REC_BF, sizeof(*bf)); bf = (struct sta_rec_bf *)tlv; /* he/eht: eBF only, except mt7992 that has 5T on 5GHz also supports iBF * vht: support eBF and iBF * ht: iBF only, since mac80211 lacks of eBF support */ if (link_sta->eht_cap.has_eht) mt7996_mcu_sta_bfer_eht(link_sta, vif, link->phy, bf, ebf); else if (link_sta->he_cap.has_he) mt7996_mcu_sta_bfer_he(link_sta, vif, link->phy, bf, ebf); else if (link_sta->vht_cap.vht_supported) mt7996_mcu_sta_bfer_vht(link_sta, link->phy, bf, ebf); else if (link_sta->ht_cap.ht_supported) mt7996_mcu_sta_bfer_ht(link_sta, link->phy, bf, ebf); else return; bf->bf_cap = ebf ? EBF_MODE : (dev->ibf ? IBF_MODE : 0); if (is_mt7992(&dev->mt76) && tx_ant == 4) bf->bf_cap |= IBF_MODE; bf->bw = link_sta->bandwidth; bf->ibf_dbw = link_sta->bandwidth; bf->ibf_nrow = tx_ant; if (link_sta->eht_cap.has_eht || link_sta->he_cap.has_he) bf->ibf_timeout = is_mt7996(&dev->mt76) ? MT7996_IBF_TIMEOUT : MT7992_IBF_TIMEOUT; else if (!ebf && link_sta->bandwidth <= IEEE80211_STA_RX_BW_40 && !bf->ncol) bf->ibf_timeout = MT7996_IBF_TIMEOUT_LEGACY; else bf->ibf_timeout = MT7996_IBF_TIMEOUT; if (bf->ncol < BF_MAT_ORDER) { if (ebf) bf->mem_20m = tx_ant < BF_MAT_ORDER ? matrix[tx_ant][bf->ncol] : 0; else bf->mem_20m = bf->nrow < BF_MAT_ORDER ? matrix[bf->nrow][bf->ncol] : 0; } switch (link_sta->bandwidth) { case IEEE80211_STA_RX_BW_160: case IEEE80211_STA_RX_BW_80: bf->mem_total = bf->mem_20m * 2; break; case IEEE80211_STA_RX_BW_40: bf->mem_total = bf->mem_20m; break; case IEEE80211_STA_RX_BW_20: default: break; } } static void mt7996_mcu_sta_bfee_tlv(struct mt7996_dev *dev, struct sk_buff *skb, struct ieee80211_bss_conf *link_conf, struct ieee80211_link_sta *link_sta, struct mt7996_vif_link *link) { struct mt7996_phy *phy = link->phy; int tx_ant = hweight8(phy->mt76->antenna_mask) - 1; struct sta_rec_bfee *bfee; struct tlv *tlv; u8 nrow = 0; if (!(link_sta->vht_cap.vht_supported || link_sta->he_cap.has_he)) return; if (!mt7996_is_ebf_supported(phy, link_conf, link_sta, true)) return; tlv = mt76_connac_mcu_add_tlv(skb, STA_REC_BFEE, sizeof(*bfee)); bfee = (struct sta_rec_bfee *)tlv; if (link_sta->he_cap.has_he) { struct ieee80211_he_cap_elem *pe = &link_sta->he_cap.he_cap_elem; nrow = HE_PHY(CAP5_BEAMFORMEE_NUM_SND_DIM_UNDER_80MHZ_MASK, pe->phy_cap_info[5]); } else if (link_sta->vht_cap.vht_supported) { struct ieee80211_sta_vht_cap *pc = &link_sta->vht_cap; nrow = FIELD_GET(IEEE80211_VHT_CAP_SOUNDING_DIMENSIONS_MASK, pc->cap); } /* reply with identity matrix to avoid 2x2 BF negative gain */ bfee->fb_identity_matrix = (nrow == 1 && tx_ant == 2); } static void mt7996_mcu_sta_tx_proc_tlv(struct sk_buff *skb) { struct sta_rec_tx_proc *tx_proc; struct tlv *tlv; tlv = mt76_connac_mcu_add_tlv(skb, STA_REC_TX_PROC, sizeof(*tx_proc)); tx_proc = (struct sta_rec_tx_proc *)tlv; tx_proc->flag = cpu_to_le32(0); } static void mt7996_mcu_sta_hdrt_tlv(struct mt7996_dev *dev, struct sk_buff *skb) { struct sta_rec_hdrt *hdrt; struct tlv *tlv; tlv = mt76_connac_mcu_add_tlv(skb, STA_REC_HDRT, sizeof(*hdrt)); hdrt = (struct sta_rec_hdrt *)tlv; hdrt->hdrt_mode = 1; } static void mt7996_mcu_sta_hdr_trans_tlv(struct mt7996_dev *dev, struct sk_buff *skb, struct ieee80211_vif *vif, struct mt76_wcid *wcid) { struct sta_rec_hdr_trans *hdr_trans; struct tlv *tlv; tlv = mt76_connac_mcu_add_tlv(skb, STA_REC_HDR_TRANS, sizeof(*hdr_trans)); hdr_trans = (struct sta_rec_hdr_trans *)tlv; hdr_trans->dis_rx_hdr_tran = true; if (vif->type == NL80211_IFTYPE_STATION) hdr_trans->to_ds = true; else hdr_trans->from_ds = true; if (!wcid) return; hdr_trans->dis_rx_hdr_tran = !test_bit(MT_WCID_FLAG_HDR_TRANS, &wcid->flags); if (test_bit(MT_WCID_FLAG_4ADDR, &wcid->flags)) { hdr_trans->to_ds = true; hdr_trans->from_ds = true; } if (vif->type == NL80211_IFTYPE_MESH_POINT) { hdr_trans->to_ds = true; hdr_trans->from_ds = true; hdr_trans->mesh = true; } } static enum mcu_mmps_mode mt7996_mcu_get_mmps_mode(enum ieee80211_smps_mode smps) { switch (smps) { case IEEE80211_SMPS_OFF: return MCU_MMPS_DISABLE; case IEEE80211_SMPS_STATIC: return MCU_MMPS_STATIC; case IEEE80211_SMPS_DYNAMIC: return MCU_MMPS_DYNAMIC; default: return MCU_MMPS_DISABLE; } } int mt7996_mcu_set_fixed_rate_ctrl(struct mt7996_dev *dev, void *data, u16 version) { struct uni_header hdr = {}; struct ra_fixed_rate *req; struct sk_buff *skb; struct tlv *tlv; int len; len = sizeof(hdr) + sizeof(*req); skb = mt76_mcu_msg_alloc(&dev->mt76, NULL, len); if (!skb) return -ENOMEM; skb_put_data(skb, &hdr, sizeof(hdr)); tlv = mt7996_mcu_add_uni_tlv(skb, UNI_RA_FIXED_RATE, sizeof(*req)); req = (struct ra_fixed_rate *)tlv; req->version = cpu_to_le16(version); memcpy(&req->rate, data, sizeof(req->rate)); return mt76_mcu_skb_send_msg(&dev->mt76, skb, MCU_WM_UNI_CMD(RA), true); } int mt7996_mcu_set_fixed_field(struct mt7996_dev *dev, struct mt7996_sta *msta, void *data, u8 link_id, u32 field) { struct mt7996_vif *mvif = msta->vif; struct mt7996_sta_link *msta_link; struct sta_rec_ra_fixed_uni *ra; struct sta_phy_uni *phy = data; struct mt76_vif_link *mlink; struct sk_buff *skb; int err = -ENODEV; struct tlv *tlv; rcu_read_lock(); mlink = rcu_dereference(mvif->mt76.link[link_id]); if (!mlink) goto error_unlock; msta_link = rcu_dereference(msta->link[link_id]); if (!msta_link) goto error_unlock; skb = __mt76_connac_mcu_alloc_sta_req(&dev->mt76, mlink, &msta_link->wcid, MT7996_STA_UPDATE_MAX_SIZE); if (IS_ERR(skb)) { err = PTR_ERR(skb); goto error_unlock; } tlv = mt76_connac_mcu_add_tlv(skb, STA_REC_RA_UPDATE, sizeof(*ra)); ra = (struct sta_rec_ra_fixed_uni *)tlv; switch (field) { case RATE_PARAM_AUTO: break; case RATE_PARAM_FIXED: case RATE_PARAM_FIXED_MCS: case RATE_PARAM_FIXED_GI: case RATE_PARAM_FIXED_HE_LTF: if (phy) ra->phy = *phy; break; case RATE_PARAM_MMPS_UPDATE: { struct ieee80211_sta *sta = wcid_to_sta(&msta_link->wcid); struct ieee80211_link_sta *link_sta; link_sta = rcu_dereference(sta->link[link_id]); if (!link_sta) { dev_kfree_skb(skb); goto error_unlock; } ra->mmps_mode = mt7996_mcu_get_mmps_mode(link_sta->smps_mode); break; } default: break; } ra->field = cpu_to_le32(field); rcu_read_unlock(); return mt76_mcu_skb_send_msg(&dev->mt76, skb, MCU_WMWA_UNI_CMD(STA_REC_UPDATE), true); error_unlock: rcu_read_unlock(); return err; } static int mt7996_mcu_add_rate_ctrl_fixed(struct mt7996_dev *dev, struct mt7996_sta *msta, struct ieee80211_vif *vif, u8 link_id) { struct ieee80211_link_sta *link_sta; struct cfg80211_bitrate_mask mask; struct mt7996_sta_link *msta_link; struct mt7996_vif_link *link; struct sta_phy_uni phy = {}; struct ieee80211_sta *sta; int ret, nrates = 0, idx; enum nl80211_band band; bool has_he; #define __sta_phy_bitrate_mask_check(_mcs, _gi, _ht, _he) \ do { \ u8 i, gi = mask.control[band]._gi; \ gi = (_he) ? gi : gi == NL80211_TXRATE_FORCE_SGI; \ phy.sgi = gi; \ phy.he_ltf = mask.control[band].he_ltf; \ for (i = 0; i < ARRAY_SIZE(mask.control[band]._mcs); i++) { \ if (!mask.control[band]._mcs[i]) \ continue; \ nrates += hweight16(mask.control[band]._mcs[i]); \ phy.mcs = ffs(mask.control[band]._mcs[i]) - 1; \ if (_ht) \ phy.mcs += 8 * i; \ } \ } while (0) rcu_read_lock(); link = mt7996_vif_link(dev, vif, link_id); if (!link) goto error_unlock; msta_link = rcu_dereference(msta->link[link_id]); if (!msta_link) goto error_unlock; sta = wcid_to_sta(&msta_link->wcid); link_sta = rcu_dereference(sta->link[link_id]); if (!link_sta) goto error_unlock; band = link->phy->mt76->chandef.chan->band; has_he = link_sta->he_cap.has_he; mask = link->bitrate_mask; idx = msta_link->wcid.idx; if (has_he) { __sta_phy_bitrate_mask_check(he_mcs, he_gi, 0, 1); } else if (link_sta->vht_cap.vht_supported) { __sta_phy_bitrate_mask_check(vht_mcs, gi, 0, 0); } else if (link_sta->ht_cap.ht_supported) { __sta_phy_bitrate_mask_check(ht_mcs, gi, 1, 0); } else { nrates = hweight32(mask.control[band].legacy); phy.mcs = ffs(mask.control[band].legacy) - 1; } rcu_read_unlock(); #undef __sta_phy_bitrate_mask_check /* fall back to auto rate control */ if (mask.control[band].gi == NL80211_TXRATE_DEFAULT_GI && mask.control[band].he_gi == GENMASK(7, 0) && mask.control[band].he_ltf == GENMASK(7, 0) && nrates != 1) return 0; /* fixed single rate */ if (nrates == 1) { ret = mt7996_mcu_set_fixed_field(dev, msta, &phy, link_id, RATE_PARAM_FIXED_MCS); if (ret) return ret; } /* fixed GI */ if (mask.control[band].gi != NL80211_TXRATE_DEFAULT_GI || mask.control[band].he_gi != GENMASK(7, 0)) { u32 addr; /* firmware updates only TXCMD but doesn't take WTBL into * account, so driver should update here to reflect the * actual txrate hardware sends out. */ addr = mt7996_mac_wtbl_lmac_addr(dev, idx, 7); if (has_he) mt76_rmw_field(dev, addr, GENMASK(31, 24), phy.sgi); else mt76_rmw_field(dev, addr, GENMASK(15, 12), phy.sgi); ret = mt7996_mcu_set_fixed_field(dev, msta, &phy, link_id, RATE_PARAM_FIXED_GI); if (ret) return ret; } /* fixed HE_LTF */ if (mask.control[band].he_ltf != GENMASK(7, 0)) { ret = mt7996_mcu_set_fixed_field(dev, msta, &phy, link_id, RATE_PARAM_FIXED_HE_LTF); if (ret) return ret; } return 0; error_unlock: rcu_read_unlock(); return -ENODEV; } static void mt7996_mcu_sta_rate_ctrl_tlv(struct sk_buff *skb, struct mt7996_dev *dev, struct ieee80211_vif *vif, struct ieee80211_bss_conf *link_conf, struct ieee80211_link_sta *link_sta, struct mt7996_vif_link *link) { #define INIT_RCPI 180 struct mt76_phy *mphy = link->phy->mt76; struct cfg80211_chan_def *chandef = &mphy->chandef; struct cfg80211_bitrate_mask *mask = &link->bitrate_mask; u32 cap = link_sta->sta->wme ? STA_CAP_WMM : 0; enum nl80211_band band = chandef->chan->band; struct sta_rec_ra_uni *ra; struct tlv *tlv; u32 supp_rate = link_sta->supp_rates[band]; tlv = mt76_connac_mcu_add_tlv(skb, STA_REC_RA, sizeof(*ra)); ra = (struct sta_rec_ra_uni *)tlv; ra->valid = true; ra->auto_rate = true; ra->phy_mode = mt76_connac_get_phy_mode(mphy, vif, band, link_sta); ra->channel = chandef->chan->hw_value; ra->bw = (link_sta->bandwidth == IEEE80211_STA_RX_BW_320) ? CMD_CBW_320MHZ : link_sta->bandwidth; ra->phy.bw = ra->bw; ra->mmps_mode = mt7996_mcu_get_mmps_mode(link_sta->smps_mode); if (supp_rate) { supp_rate &= mask->control[band].legacy; ra->rate_len = hweight32(supp_rate); if (band == NL80211_BAND_2GHZ) { ra->supp_mode = MODE_CCK; ra->supp_cck_rate = supp_rate & GENMASK(3, 0); if (ra->rate_len > 4) { ra->supp_mode |= MODE_OFDM; ra->supp_ofdm_rate = supp_rate >> 4; } } else { ra->supp_mode = MODE_OFDM; ra->supp_ofdm_rate = supp_rate; } } if (link_sta->ht_cap.ht_supported) { ra->supp_mode |= MODE_HT; ra->af = link_sta->ht_cap.ampdu_factor; ra->ht_gf = !!(link_sta->ht_cap.cap & IEEE80211_HT_CAP_GRN_FLD); cap |= STA_CAP_HT; if (link_sta->ht_cap.cap & IEEE80211_HT_CAP_SGI_20) cap |= STA_CAP_SGI_20; if (link_sta->ht_cap.cap & IEEE80211_HT_CAP_SGI_40) cap |= STA_CAP_SGI_40; if (link_sta->ht_cap.cap & IEEE80211_HT_CAP_TX_STBC) cap |= STA_CAP_TX_STBC; if (link_sta->ht_cap.cap & IEEE80211_HT_CAP_RX_STBC) cap |= STA_CAP_RX_STBC; if (link_conf->ht_ldpc && (link_sta->ht_cap.cap & IEEE80211_HT_CAP_LDPC_CODING)) cap |= STA_CAP_LDPC; mt7996_mcu_set_sta_ht_mcs(link_sta, ra->ht_mcs, mask->control[band].ht_mcs); ra->supp_ht_mcs = *(__le32 *)ra->ht_mcs; } if (link_sta->vht_cap.vht_supported) { u8 af; ra->supp_mode |= MODE_VHT; af = FIELD_GET(IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_MASK, link_sta->vht_cap.cap); ra->af = max_t(u8, ra->af, af); cap |= STA_CAP_VHT; if (link_sta->vht_cap.cap & IEEE80211_VHT_CAP_SHORT_GI_80) cap |= STA_CAP_VHT_SGI_80; if (link_sta->vht_cap.cap & IEEE80211_VHT_CAP_SHORT_GI_160) cap |= STA_CAP_VHT_SGI_160; if (link_sta->vht_cap.cap & IEEE80211_VHT_CAP_TXSTBC) cap |= STA_CAP_VHT_TX_STBC; if (link_sta->vht_cap.cap & IEEE80211_VHT_CAP_RXSTBC_1) cap |= STA_CAP_VHT_RX_STBC; if ((vif->type != NL80211_IFTYPE_AP || link_conf->vht_ldpc) && (link_sta->vht_cap.cap & IEEE80211_VHT_CAP_RXLDPC)) cap |= STA_CAP_VHT_LDPC; mt7996_mcu_set_sta_vht_mcs(link_sta, ra->supp_vht_mcs, mask->control[band].vht_mcs); } if (link_sta->he_cap.has_he) { ra->supp_mode |= MODE_HE; cap |= STA_CAP_HE; if (link_sta->he_6ghz_capa.capa) ra->af = le16_get_bits(link_sta->he_6ghz_capa.capa, IEEE80211_HE_6GHZ_CAP_MAX_AMPDU_LEN_EXP); } ra->sta_cap = cpu_to_le32(cap); memset(ra->rx_rcpi, INIT_RCPI, sizeof(ra->rx_rcpi)); } int mt7996_mcu_add_rate_ctrl(struct mt7996_dev *dev, struct mt7996_sta *msta, struct ieee80211_vif *vif, u8 link_id, bool changed) { struct ieee80211_bss_conf *link_conf; struct ieee80211_link_sta *link_sta; struct mt7996_sta_link *msta_link; struct mt7996_vif_link *link; struct ieee80211_sta *sta; struct sk_buff *skb; int ret = -ENODEV; rcu_read_lock(); link = mt7996_vif_link(dev, vif, link_id); if (!link) goto error_unlock; msta_link = rcu_dereference(msta->link[link_id]); if (!msta_link) goto error_unlock; sta = wcid_to_sta(&msta_link->wcid); link_sta = rcu_dereference(sta->link[link_id]); if (!link_sta) goto error_unlock; link_conf = rcu_dereference(vif->link_conf[link_id]); if (!link_conf) goto error_unlock; skb = __mt76_connac_mcu_alloc_sta_req(&dev->mt76, &link->mt76, &msta_link->wcid, MT7996_STA_UPDATE_MAX_SIZE); if (IS_ERR(skb)) { ret = PTR_ERR(skb); goto error_unlock; } /* firmware rc algorithm refers to sta_rec_he for HE control. * once dev->rc_work changes the settings driver should also * update sta_rec_he here. */ if (changed) mt7996_mcu_sta_he_tlv(skb, link_sta, link); /* sta_rec_ra accommodates BW, NSS and only MCS range format * i.e 0-{7,8,9} for VHT. */ mt7996_mcu_sta_rate_ctrl_tlv(skb, dev, vif, link_conf, link_sta, link); rcu_read_unlock(); ret = mt76_mcu_skb_send_msg(&dev->mt76, skb, MCU_WMWA_UNI_CMD(STA_REC_UPDATE), true); if (ret) return ret; return mt7996_mcu_add_rate_ctrl_fixed(dev, msta, vif, link_id); error_unlock: rcu_read_unlock(); return ret; } static int mt7996_mcu_add_group(struct mt7996_dev *dev, struct ieee80211_vif *vif, struct ieee80211_sta *sta) { #define MT_STA_BSS_GROUP 1 struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv; struct mt7996_sta_link *msta_link; struct mt7996_sta *msta; struct { u8 __rsv1[4]; __le16 tag; __le16 len; __le16 wlan_idx; u8 __rsv2[2]; __le32 action; __le32 val; u8 __rsv3[8]; } __packed req = { .tag = cpu_to_le16(UNI_VOW_DRR_CTRL), .len = cpu_to_le16(sizeof(req) - 4), .action = cpu_to_le32(MT_STA_BSS_GROUP), .val = cpu_to_le32(mvif->deflink.mt76.idx % 16), }; msta = sta ? (struct mt7996_sta *)sta->drv_priv : NULL; msta_link = msta ? &msta->deflink : &mvif->deflink.msta_link; req.wlan_idx = cpu_to_le16(msta_link->wcid.idx); return mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(VOW), &req, sizeof(req), true); } static void mt7996_mcu_sta_mld_setup_tlv(struct mt7996_dev *dev, struct sk_buff *skb, struct ieee80211_vif *vif, struct ieee80211_sta *sta) { struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv; unsigned int nlinks = hweight16(sta->valid_links); struct mld_setup_link *mld_setup_link; struct ieee80211_link_sta *link_sta; struct sta_rec_mld_setup *mld_setup; struct mt7996_sta_link *msta_link; unsigned int link_id; struct tlv *tlv; msta_link = mt76_dereference(msta->link[msta->deflink_id], &dev->mt76); if (!msta_link) return; tlv = mt76_connac_mcu_add_tlv(skb, STA_REC_MLD, sizeof(struct sta_rec_mld_setup) + sizeof(struct mld_setup_link) * nlinks); mld_setup = (struct sta_rec_mld_setup *)tlv; memcpy(mld_setup->mld_addr, sta->addr, ETH_ALEN); mld_setup->setup_wcid = cpu_to_le16(msta_link->wcid.idx); mld_setup->primary_id = cpu_to_le16(msta_link->wcid.idx); if (nlinks > 1) { link_id = __ffs(sta->valid_links & ~BIT(msta->deflink_id)); msta_link = mt76_dereference(msta->link[link_id], &dev->mt76); if (!msta_link) return; } mld_setup->seconed_id = cpu_to_le16(msta_link->wcid.idx); mld_setup->link_num = nlinks; mld_setup_link = (struct mld_setup_link *)mld_setup->link_info; for_each_sta_active_link(vif, sta, link_sta, link_id) { struct mt7996_vif_link *link; msta_link = mt76_dereference(msta->link[link_id], &dev->mt76); if (!msta_link) continue; link = mt7996_vif_link(dev, vif, link_id); if (!link) continue; mld_setup_link->wcid = cpu_to_le16(msta_link->wcid.idx); mld_setup_link->bss_idx = link->mt76.idx; mld_setup_link++; } } static void mt7996_mcu_sta_eht_mld_tlv(struct mt7996_dev *dev, struct sk_buff *skb, struct ieee80211_sta *sta) { struct sta_rec_eht_mld *eht_mld; struct tlv *tlv; int i; tlv = mt76_connac_mcu_add_tlv(skb, STA_REC_EHT_MLD, sizeof(*eht_mld)); eht_mld = (struct sta_rec_eht_mld *)tlv; for (i = 0; i < ARRAY_SIZE(eht_mld->str_cap); i++) eht_mld->str_cap[i] = 0x7; } int mt7996_mcu_add_sta(struct mt7996_dev *dev, struct ieee80211_bss_conf *link_conf, struct ieee80211_link_sta *link_sta, struct mt7996_vif_link *link, struct mt7996_sta_link *msta_link, int conn_state, bool newly) { struct mt76_wcid *wcid = msta_link ? &msta_link->wcid : link->mt76.wcid; struct ieee80211_sta *sta = link_sta ? link_sta->sta : NULL; struct sk_buff *skb; int ret; skb = __mt76_connac_mcu_alloc_sta_req(&dev->mt76, &link->mt76, wcid, MT7996_STA_UPDATE_MAX_SIZE); if (IS_ERR(skb)) return PTR_ERR(skb); /* starec basic */ mt76_connac_mcu_sta_basic_tlv(&dev->mt76, skb, link_conf, link_sta, conn_state, newly); if (conn_state == CONN_STATE_DISCONNECT) goto out; /* starec hdr trans */ mt7996_mcu_sta_hdr_trans_tlv(dev, skb, link_conf->vif, wcid); /* starec tx proc */ mt7996_mcu_sta_tx_proc_tlv(skb); /* tag order is in accordance with firmware dependency. */ if (link_sta) { /* starec hdrt mode */ mt7996_mcu_sta_hdrt_tlv(dev, skb); if (conn_state == CONN_STATE_CONNECT) { /* starec bfer */ mt7996_mcu_sta_bfer_tlv(dev, skb, link_conf, link_sta, link); /* starec bfee */ mt7996_mcu_sta_bfee_tlv(dev, skb, link_conf, link_sta, link); } /* starec ht */ mt7996_mcu_sta_ht_tlv(skb, link_sta); /* starec vht */ mt7996_mcu_sta_vht_tlv(skb, link_sta); /* starec uapsd */ mt76_connac_mcu_sta_uapsd(skb, link_conf->vif, sta); /* starec amsdu */ mt7996_mcu_sta_amsdu_tlv(dev, skb, link_conf->vif, link_sta, msta_link); /* starec he */ mt7996_mcu_sta_he_tlv(skb, link_sta, link); /* starec he 6g*/ mt7996_mcu_sta_he_6g_tlv(skb, link_sta); /* starec eht */ mt7996_mcu_sta_eht_tlv(skb, link_sta); /* starec muru */ mt7996_mcu_sta_muru_tlv(dev, skb, link_conf, link_sta); if (sta->mlo) { mt7996_mcu_sta_mld_setup_tlv(dev, skb, link_conf->vif, sta); mt7996_mcu_sta_eht_mld_tlv(dev, skb, sta); } } ret = mt7996_mcu_add_group(dev, link_conf->vif, sta); if (ret) { dev_kfree_skb(skb); return ret; } out: return mt76_mcu_skb_send_msg(&dev->mt76, skb, MCU_WMWA_UNI_CMD(STA_REC_UPDATE), true); } int mt7996_mcu_teardown_mld_sta(struct mt7996_dev *dev, struct mt7996_vif_link *link, struct mt7996_sta_link *msta_link) { struct sk_buff *skb; skb = __mt76_connac_mcu_alloc_sta_req(&dev->mt76, &link->mt76, &msta_link->wcid, MT7996_STA_UPDATE_MAX_SIZE); if (IS_ERR(skb)) return PTR_ERR(skb); mt76_connac_mcu_add_tlv(skb, STA_REC_MLD_OFF, sizeof(struct tlv)); return mt76_mcu_skb_send_msg(&dev->mt76, skb, MCU_WMWA_UNI_CMD(STA_REC_UPDATE), true); } static int mt7996_mcu_sta_key_tlv(struct mt76_wcid *wcid, struct sk_buff *skb, struct ieee80211_key_conf *key, enum set_key_cmd cmd) { struct sta_rec_sec_uni *sec; struct tlv *tlv; tlv = mt76_connac_mcu_add_tlv(skb, STA_REC_KEY_V2, sizeof(*sec)); sec = (struct sta_rec_sec_uni *)tlv; sec->add = cmd; if (cmd == SET_KEY) { struct sec_key_uni *sec_key; u8 cipher; cipher = mt76_connac_mcu_get_cipher(key->cipher); if (cipher == MCU_CIPHER_NONE) return -EOPNOTSUPP; sec_key = &sec->key[0]; sec_key->wlan_idx = cpu_to_le16(wcid->idx); sec_key->mgmt_prot = 0; sec_key->cipher_id = cipher; sec_key->cipher_len = sizeof(*sec_key); sec_key->key_id = key->keyidx; sec_key->key_len = key->keylen; sec_key->need_resp = 0; memcpy(sec_key->key, key->key, key->keylen); if (cipher == MCU_CIPHER_TKIP) { /* Rx/Tx MIC keys are swapped */ memcpy(sec_key->key + 16, key->key + 24, 8); memcpy(sec_key->key + 24, key->key + 16, 8); } sec->n_cipher = 1; } else { sec->n_cipher = 0; } return 0; } int mt7996_mcu_add_key(struct mt76_dev *dev, struct ieee80211_vif *vif, struct ieee80211_key_conf *key, int mcu_cmd, struct mt76_wcid *wcid, enum set_key_cmd cmd) { struct mt76_vif_link *mvif = (struct mt76_vif_link *)vif->drv_priv; struct sk_buff *skb; int ret; skb = __mt76_connac_mcu_alloc_sta_req(dev, mvif, wcid, MT7996_STA_UPDATE_MAX_SIZE); if (IS_ERR(skb)) return PTR_ERR(skb); ret = mt7996_mcu_sta_key_tlv(wcid, skb, key, cmd); if (ret) return ret; return mt76_mcu_skb_send_msg(dev, skb, mcu_cmd, true); } static int mt7996_mcu_get_pn(struct mt7996_dev *dev, struct mt7996_vif_link *link, struct mt7996_sta_link *msta_link, u8 *pn) { #define TSC_TYPE_BIGTK_PN 2 struct sta_rec_pn_info *pn_info; struct sk_buff *skb, *rskb; struct tlv *tlv; int ret; skb = mt76_connac_mcu_alloc_sta_req(&dev->mt76, &link->mt76, &msta_link->wcid); if (IS_ERR(skb)) return PTR_ERR(skb); tlv = mt76_connac_mcu_add_tlv(skb, STA_REC_PN_INFO, sizeof(*pn_info)); pn_info = (struct sta_rec_pn_info *)tlv; pn_info->tsc_type = TSC_TYPE_BIGTK_PN; ret = mt76_mcu_skb_send_and_get_msg(&dev->mt76, skb, MCU_WM_UNI_CMD_QUERY(STA_REC_UPDATE), true, &rskb); if (ret) return ret; skb_pull(rskb, 4); pn_info = (struct sta_rec_pn_info *)rskb->data; if (le16_to_cpu(pn_info->tag) == STA_REC_PN_INFO) memcpy(pn, pn_info->pn, 6); dev_kfree_skb(rskb); return 0; } int mt7996_mcu_bcn_prot_enable(struct mt7996_dev *dev, struct mt7996_vif_link *link, struct mt7996_sta_link *msta_link, struct ieee80211_key_conf *key) { struct mt7996_mcu_bcn_prot_tlv *bcn_prot; struct sk_buff *skb; struct tlv *tlv; u8 pn[6] = {}; int len = sizeof(struct bss_req_hdr) + sizeof(struct mt7996_mcu_bcn_prot_tlv); int ret; skb = __mt7996_mcu_alloc_bss_req(&dev->mt76, &link->mt76, len); if (IS_ERR(skb)) return PTR_ERR(skb); tlv = mt76_connac_mcu_add_tlv(skb, UNI_BSS_INFO_BCN_PROT, sizeof(*bcn_prot)); bcn_prot = (struct mt7996_mcu_bcn_prot_tlv *)tlv; ret = mt7996_mcu_get_pn(dev, link, msta_link, pn); if (ret) { dev_kfree_skb(skb); return ret; } switch (key->cipher) { case WLAN_CIPHER_SUITE_AES_CMAC: bcn_prot->cipher_id = MCU_CIPHER_BCN_PROT_CMAC_128; break; case WLAN_CIPHER_SUITE_BIP_GMAC_128: bcn_prot->cipher_id = MCU_CIPHER_BCN_PROT_GMAC_128; break; case WLAN_CIPHER_SUITE_BIP_GMAC_256: bcn_prot->cipher_id = MCU_CIPHER_BCN_PROT_GMAC_256; break; case WLAN_CIPHER_SUITE_BIP_CMAC_256: default: dev_err(dev->mt76.dev, "Not supported Bigtk Cipher\n"); dev_kfree_skb(skb); return -EOPNOTSUPP; } pn[0]++; memcpy(bcn_prot->pn, pn, 6); bcn_prot->enable = BP_SW_MODE; memcpy(bcn_prot->key, key->key, WLAN_MAX_KEY_LEN); bcn_prot->key_id = key->keyidx; return mt76_mcu_skb_send_msg(&dev->mt76, skb, MCU_WMWA_UNI_CMD(BSS_INFO_UPDATE), true); } int mt7996_mcu_add_dev_info(struct mt7996_phy *phy, struct ieee80211_vif *vif, struct ieee80211_bss_conf *link_conf, struct mt76_vif_link *mlink, bool enable) { struct mt7996_dev *dev = phy->dev; struct { struct req_hdr { u8 omac_idx; u8 band_idx; u8 __rsv[2]; } __packed hdr; struct req_tlv { __le16 tag; __le16 len; u8 active; u8 __rsv; u8 omac_addr[ETH_ALEN]; } __packed tlv; } data = { .hdr = { .omac_idx = mlink->omac_idx, .band_idx = mlink->band_idx, }, .tlv = { .tag = cpu_to_le16(DEV_INFO_ACTIVE), .len = cpu_to_le16(sizeof(struct req_tlv)), .active = enable, }, }; if (mlink->omac_idx >= REPEATER_BSSID_START) return mt7996_mcu_muar_config(dev, mlink, link_conf->addr, false, enable); memcpy(data.tlv.omac_addr, link_conf->addr, ETH_ALEN); return mt76_mcu_send_msg(&dev->mt76, MCU_WMWA_UNI_CMD(DEV_INFO_UPDATE), &data, sizeof(data), true); } static void mt7996_mcu_beacon_cntdwn(struct sk_buff *rskb, struct sk_buff *skb, struct ieee80211_mutable_offsets *offs, bool csa) { struct bss_bcn_cntdwn_tlv *info; struct tlv *tlv; u16 tag; if (!offs->cntdwn_counter_offs[0]) return; tag = csa ? UNI_BSS_INFO_BCN_CSA : UNI_BSS_INFO_BCN_BCC; tlv = mt7996_mcu_add_uni_tlv(rskb, tag, sizeof(*info)); info = (struct bss_bcn_cntdwn_tlv *)tlv; info->cnt = skb->data[offs->cntdwn_counter_offs[0]]; } static void mt7996_mcu_beacon_mbss(struct sk_buff *rskb, struct sk_buff *skb, struct bss_bcn_content_tlv *bcn, struct ieee80211_mutable_offsets *offs) { struct bss_bcn_mbss_tlv *mbss; const struct element *elem; struct tlv *tlv; tlv = mt7996_mcu_add_uni_tlv(rskb, UNI_BSS_INFO_BCN_MBSSID, sizeof(*mbss)); mbss = (struct bss_bcn_mbss_tlv *)tlv; mbss->offset[0] = cpu_to_le16(offs->tim_offset); mbss->bitmap = cpu_to_le32(1); for_each_element_id(elem, WLAN_EID_MULTIPLE_BSSID, &skb->data[offs->mbssid_off], skb->len - offs->mbssid_off) { const struct element *sub_elem; if (elem->datalen < 2) continue; for_each_element(sub_elem, elem->data + 1, elem->datalen - 1) { const struct ieee80211_bssid_index *idx; const u8 *idx_ie; /* not a valid BSS profile */ if (sub_elem->id || sub_elem->datalen < 4) continue; /* Find WLAN_EID_MULTI_BSSID_IDX * in the merged nontransmitted profile */ idx_ie = cfg80211_find_ie(WLAN_EID_MULTI_BSSID_IDX, sub_elem->data, sub_elem->datalen); if (!idx_ie || idx_ie[1] < sizeof(*idx)) continue; #if defined(__linux__) idx = (void *)(idx_ie + 2); #elif defined(__FreeBSD__) idx = (const void *)(idx_ie + 2); #endif if (!idx->bssid_index || idx->bssid_index > 31) continue; mbss->offset[idx->bssid_index] = cpu_to_le16(idx_ie - skb->data); mbss->bitmap |= cpu_to_le32(BIT(idx->bssid_index)); } } } static void mt7996_mcu_beacon_cont(struct mt7996_dev *dev, struct ieee80211_bss_conf *link_conf, struct sk_buff *rskb, struct sk_buff *skb, struct bss_bcn_content_tlv *bcn, struct ieee80211_mutable_offsets *offs) { struct mt76_wcid *wcid = &dev->mt76.global_wcid; u8 *buf; bcn->pkt_len = cpu_to_le16(MT_TXD_SIZE + skb->len); bcn->tim_ie_pos = cpu_to_le16(offs->tim_offset); if (offs->cntdwn_counter_offs[0]) { u16 offset = offs->cntdwn_counter_offs[0]; if (link_conf->csa_active) bcn->csa_ie_pos = cpu_to_le16(offset - 4); if (link_conf->color_change_active) bcn->bcc_ie_pos = cpu_to_le16(offset - 3); } buf = (u8 *)bcn + sizeof(*bcn); mt7996_mac_write_txwi(dev, (__le32 *)buf, skb, wcid, NULL, 0, 0, BSS_CHANGED_BEACON); memcpy(buf + MT_TXD_SIZE, skb->data, skb->len); } int mt7996_mcu_add_beacon(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ieee80211_bss_conf *link_conf) { struct mt7996_dev *dev = mt7996_hw_dev(hw); struct mt7996_vif_link *link = mt7996_vif_conf_link(dev, vif, link_conf); struct mt76_vif_link *mlink = link ? &link->mt76 : NULL; struct ieee80211_mutable_offsets offs; struct ieee80211_tx_info *info; struct sk_buff *skb, *rskb; struct tlv *tlv; struct bss_bcn_content_tlv *bcn; int len, extra_len = 0; bool enabled = link_conf->enable_beacon; if (link_conf->nontransmitted) return 0; if (!mlink) return -EINVAL; if (link->phy && link->phy->mt76->offchannel) enabled = false; rskb = __mt7996_mcu_alloc_bss_req(&dev->mt76, mlink, MT7996_MAX_BSS_OFFLOAD_SIZE); if (IS_ERR(rskb)) return PTR_ERR(rskb); skb = ieee80211_beacon_get_template(hw, vif, &offs, link_conf->link_id); if (enabled && !skb) { dev_kfree_skb(rskb); return -EINVAL; } if (skb) { if (skb->len > MT7996_MAX_BEACON_SIZE) { dev_err(dev->mt76.dev, "Bcn size limit exceed\n"); dev_kfree_skb(rskb); dev_kfree_skb(skb); return -EINVAL; } extra_len = skb->len; } len = ALIGN(sizeof(*bcn) + MT_TXD_SIZE + extra_len, 4); tlv = mt7996_mcu_add_uni_tlv(rskb, UNI_BSS_INFO_BCN_CONTENT, len); bcn = (struct bss_bcn_content_tlv *)tlv; bcn->enable = enabled; if (!bcn->enable) goto out; info = IEEE80211_SKB_CB(skb); info->hw_queue |= FIELD_PREP(MT_TX_HW_QUEUE_PHY, mlink->band_idx); mt7996_mcu_beacon_cont(dev, link_conf, rskb, skb, bcn, &offs); if (link_conf->bssid_indicator) mt7996_mcu_beacon_mbss(rskb, skb, bcn, &offs); mt7996_mcu_beacon_cntdwn(rskb, skb, &offs, link_conf->csa_active); out: dev_kfree_skb(skb); return mt76_mcu_skb_send_msg(&dev->mt76, rskb, MCU_WMWA_UNI_CMD(BSS_INFO_UPDATE), true); } int mt7996_mcu_beacon_inband_discov(struct mt7996_dev *dev, struct ieee80211_bss_conf *link_conf, struct mt7996_vif_link *link, u32 changed) { #define OFFLOAD_TX_MODE_SU BIT(0) #define OFFLOAD_TX_MODE_MU BIT(1) struct ieee80211_vif *vif = link_conf->vif; struct ieee80211_hw *hw = mt76_hw(dev); struct mt7996_phy *phy = link->phy; struct mt76_wcid *wcid = &dev->mt76.global_wcid; struct bss_inband_discovery_tlv *discov; struct ieee80211_tx_info *info; struct sk_buff *rskb, *skb = NULL; struct cfg80211_chan_def *chandef; enum nl80211_band band; struct tlv *tlv; u8 *buf, interval; int len; if (!phy) return -EINVAL; chandef = &phy->mt76->chandef; band = chandef->chan->band; if (link_conf->nontransmitted) return 0; rskb = __mt7996_mcu_alloc_bss_req(&dev->mt76, &link->mt76, MT7996_MAX_BSS_OFFLOAD_SIZE); if (IS_ERR(rskb)) return PTR_ERR(rskb); if (changed & BSS_CHANGED_FILS_DISCOVERY && link_conf->fils_discovery.max_interval) { interval = link_conf->fils_discovery.max_interval; skb = ieee80211_get_fils_discovery_tmpl(hw, vif); } else if (changed & BSS_CHANGED_UNSOL_BCAST_PROBE_RESP && link_conf->unsol_bcast_probe_resp_interval) { interval = link_conf->unsol_bcast_probe_resp_interval; skb = ieee80211_get_unsol_bcast_probe_resp_tmpl(hw, vif); } if (!skb) { dev_kfree_skb(rskb); return -EINVAL; } if (skb->len > MT7996_MAX_BEACON_SIZE) { dev_err(dev->mt76.dev, "inband discovery size limit exceed\n"); dev_kfree_skb(rskb); dev_kfree_skb(skb); return -EINVAL; } info = IEEE80211_SKB_CB(skb); info->control.vif = vif; info->band = band; info->hw_queue |= FIELD_PREP(MT_TX_HW_QUEUE_PHY, phy->mt76->band_idx); len = ALIGN(sizeof(*discov) + MT_TXD_SIZE + skb->len, 4); tlv = mt7996_mcu_add_uni_tlv(rskb, UNI_BSS_INFO_OFFLOAD, len); discov = (struct bss_inband_discovery_tlv *)tlv; discov->tx_mode = OFFLOAD_TX_MODE_SU; /* 0: UNSOL PROBE RESP, 1: FILS DISCOV */ discov->tx_type = !!(changed & BSS_CHANGED_FILS_DISCOVERY); discov->tx_interval = interval; discov->prob_rsp_len = cpu_to_le16(MT_TXD_SIZE + skb->len); discov->enable = true; discov->wcid = cpu_to_le16(MT7996_WTBL_RESERVED); buf = (u8 *)tlv + sizeof(*discov); mt7996_mac_write_txwi(dev, (__le32 *)buf, skb, wcid, NULL, 0, 0, changed); memcpy(buf + MT_TXD_SIZE, skb->data, skb->len); dev_kfree_skb(skb); return mt76_mcu_skb_send_msg(&dev->mt76, rskb, MCU_WMWA_UNI_CMD(BSS_INFO_UPDATE), true); } static int mt7996_driver_own(struct mt7996_dev *dev, u8 band) { mt76_wr(dev, MT_TOP_LPCR_HOST_BAND(band), MT_TOP_LPCR_HOST_DRV_OWN); if (!mt76_poll_msec(dev, MT_TOP_LPCR_HOST_BAND(band), MT_TOP_LPCR_HOST_FW_OWN_STAT, 0, 500)) { dev_err(dev->mt76.dev, "Timeout for driver own\n"); return -EIO; } /* clear irq when the driver own success */ mt76_wr(dev, MT_TOP_LPCR_HOST_BAND_IRQ_STAT(band), MT_TOP_LPCR_HOST_BAND_STAT); return 0; } static u32 mt7996_patch_sec_mode(u32 key_info) { u32 sec = u32_get_bits(key_info, MT7996_PATCH_SEC), key = 0; if (key_info == GENMASK(31, 0) || sec == MT7996_SEC_MODE_PLAIN) return 0; if (sec == MT7996_SEC_MODE_AES) key = u32_get_bits(key_info, MT7996_PATCH_AES_KEY); else key = u32_get_bits(key_info, MT7996_PATCH_SCRAMBLE_KEY); return MT7996_SEC_ENCRYPT | MT7996_SEC_IV | u32_encode_bits(key, MT7996_SEC_KEY_IDX); } static int mt7996_load_patch(struct mt7996_dev *dev) { const struct mt7996_patch_hdr *hdr; const struct firmware *fw = NULL; int i, ret, sem; sem = mt76_connac_mcu_patch_sem_ctrl(&dev->mt76, 1); switch (sem) { case PATCH_IS_DL: return 0; case PATCH_NOT_DL_SEM_SUCCESS: break; default: dev_err(dev->mt76.dev, "Failed to get patch semaphore\n"); return -EAGAIN; } ret = request_firmware(&fw, fw_name(dev, ROM_PATCH), dev->mt76.dev); if (ret) goto out; if (!fw || !fw->data || fw->size < sizeof(*hdr)) { dev_err(dev->mt76.dev, "Invalid firmware\n"); ret = -EINVAL; goto out; } hdr = (const struct mt7996_patch_hdr *)(fw->data); dev_info(dev->mt76.dev, "HW/SW Version: 0x%x, Build Time: %.16s\n", be32_to_cpu(hdr->hw_sw_ver), hdr->build_date); for (i = 0; i < be32_to_cpu(hdr->desc.n_region); i++) { #if defined(__linux__) struct mt7996_patch_sec *sec; #elif defined(__FreeBSD__) const struct mt7996_patch_sec *sec; #endif const u8 *dl; u32 len, addr, sec_key_idx, mode = DL_MODE_NEED_RSP; #if defined(__linux__) sec = (struct mt7996_patch_sec *)(fw->data + sizeof(*hdr) + #elif defined(__FreeBSD__) sec = (const struct mt7996_patch_sec *)(fw->data + sizeof(*hdr) + #endif i * sizeof(*sec)); if ((be32_to_cpu(sec->type) & PATCH_SEC_TYPE_MASK) != PATCH_SEC_TYPE_INFO) { ret = -EINVAL; goto out; } addr = be32_to_cpu(sec->info.addr); len = be32_to_cpu(sec->info.len); sec_key_idx = be32_to_cpu(sec->info.sec_key_idx); dl = fw->data + be32_to_cpu(sec->offs); mode |= mt7996_patch_sec_mode(sec_key_idx); ret = mt76_connac_mcu_init_download(&dev->mt76, addr, len, mode); if (ret) { dev_err(dev->mt76.dev, "Download request failed\n"); goto out; } ret = __mt76_mcu_send_firmware(&dev->mt76, MCU_CMD(FW_SCATTER), dl, len, 4096); if (ret) { dev_err(dev->mt76.dev, "Failed to send patch\n"); goto out; } } ret = mt76_connac_mcu_start_patch(&dev->mt76); if (ret) dev_err(dev->mt76.dev, "Failed to start patch\n"); out: sem = mt76_connac_mcu_patch_sem_ctrl(&dev->mt76, 0); switch (sem) { case PATCH_REL_SEM_SUCCESS: break; default: ret = -EAGAIN; dev_err(dev->mt76.dev, "Failed to release patch semaphore\n"); break; } release_firmware(fw); return ret; } static int mt7996_mcu_send_ram_firmware(struct mt7996_dev *dev, const struct mt7996_fw_trailer *hdr, const u8 *data, enum mt7996_ram_type type) { int i, offset = 0; u32 override = 0, option = 0; for (i = 0; i < hdr->n_region; i++) { const struct mt7996_fw_region *region; int err; u32 len, addr, mode; region = (const struct mt7996_fw_region *)((const u8 *)hdr - (hdr->n_region - i) * sizeof(*region)); /* DSP and WA use same mode */ mode = mt76_connac_mcu_gen_dl_mode(&dev->mt76, region->feature_set, type != MT7996_RAM_TYPE_WM); len = le32_to_cpu(region->len); addr = le32_to_cpu(region->addr); if (region->feature_set & FW_FEATURE_OVERRIDE_ADDR) override = addr; err = mt76_connac_mcu_init_download(&dev->mt76, addr, len, mode); if (err) { dev_err(dev->mt76.dev, "Download request failed\n"); return err; } err = __mt76_mcu_send_firmware(&dev->mt76, MCU_CMD(FW_SCATTER), data + offset, len, 4096); if (err) { dev_err(dev->mt76.dev, "Failed to send firmware.\n"); return err; } offset += len; } if (override) option |= FW_START_OVERRIDE; if (type == MT7996_RAM_TYPE_WA) option |= FW_START_WORKING_PDA_CR4; else if (type == MT7996_RAM_TYPE_DSP) option |= FW_START_WORKING_PDA_DSP; return mt76_connac_mcu_start_firmware(&dev->mt76, override, option); } static int __mt7996_load_ram(struct mt7996_dev *dev, const char *fw_type, const char *fw_file, enum mt7996_ram_type ram_type) { const struct mt7996_fw_trailer *hdr; const struct firmware *fw; int ret; ret = request_firmware(&fw, fw_file, dev->mt76.dev); if (ret) return ret; if (!fw || !fw->data || fw->size < sizeof(*hdr)) { dev_err(dev->mt76.dev, "Invalid firmware\n"); ret = -EINVAL; goto out; } hdr = (const void *)(fw->data + fw->size - sizeof(*hdr)); dev_info(dev->mt76.dev, "%s Firmware Version: %.10s, Build Time: %.15s\n", fw_type, hdr->fw_ver, hdr->build_date); ret = mt7996_mcu_send_ram_firmware(dev, hdr, fw->data, ram_type); if (ret) { dev_err(dev->mt76.dev, "Failed to start %s firmware\n", fw_type); goto out; } snprintf(dev->mt76.hw->wiphy->fw_version, sizeof(dev->mt76.hw->wiphy->fw_version), "%.10s-%.15s", hdr->fw_ver, hdr->build_date); out: release_firmware(fw); return ret; } static int mt7996_load_ram(struct mt7996_dev *dev) { int ret; ret = __mt7996_load_ram(dev, "WM", fw_name(dev, FIRMWARE_WM), MT7996_RAM_TYPE_WM); if (ret) return ret; if (!mt7996_has_wa(dev)) return 0; ret = __mt7996_load_ram(dev, "DSP", fw_name(dev, FIRMWARE_DSP), MT7996_RAM_TYPE_DSP); if (ret) return ret; return __mt7996_load_ram(dev, "WA", fw_name(dev, FIRMWARE_WA), MT7996_RAM_TYPE_WA); } static int mt7996_firmware_state(struct mt7996_dev *dev, u8 fw_state) { u32 state = FIELD_PREP(MT_TOP_MISC_FW_STATE, fw_state); if (!mt76_poll_msec(dev, MT_TOP_MISC, MT_TOP_MISC_FW_STATE, state, 1000)) { dev_err(dev->mt76.dev, "Timeout for initializing firmware\n"); return -EIO; } return 0; } static int mt7996_mcu_restart(struct mt76_dev *dev) { struct { u8 __rsv1[4]; __le16 tag; __le16 len; u8 power_mode; u8 __rsv2[3]; } __packed req = { .tag = cpu_to_le16(UNI_POWER_OFF), .len = cpu_to_le16(sizeof(req) - 4), .power_mode = 1, }; return mt76_mcu_send_msg(dev, MCU_WM_UNI_CMD(POWER_CTRL), &req, sizeof(req), false); } static int mt7996_load_firmware(struct mt7996_dev *dev) { u8 fw_state; int ret; /* make sure fw is download state */ if (mt7996_firmware_state(dev, FW_STATE_FW_DOWNLOAD)) { /* restart firmware once */ mt7996_mcu_restart(&dev->mt76); ret = mt7996_firmware_state(dev, FW_STATE_FW_DOWNLOAD); if (ret) { dev_err(dev->mt76.dev, "Firmware is not ready for download\n"); return ret; } } ret = mt7996_load_patch(dev); if (ret) return ret; ret = mt7996_load_ram(dev); if (ret) return ret; fw_state = mt7996_has_wa(dev) ? FW_STATE_RDY : FW_STATE_NORMAL_TRX; ret = mt7996_firmware_state(dev, fw_state); if (ret) return ret; mt76_queue_tx_cleanup(dev, dev->mt76.q_mcu[MT_MCUQ_FWDL], false); dev_dbg(dev->mt76.dev, "Firmware init done\n"); return 0; } int mt7996_mcu_fw_log_2_host(struct mt7996_dev *dev, u8 type, u8 ctrl) { struct { u8 _rsv[4]; __le16 tag; __le16 len; u8 ctrl; u8 interval; u8 _rsv2[2]; } __packed data = { .tag = cpu_to_le16(UNI_WSYS_CONFIG_FW_LOG_CTRL), .len = cpu_to_le16(sizeof(data) - 4), .ctrl = ctrl, }; if (type == MCU_FW_LOG_WA) return mt76_mcu_send_msg(&dev->mt76, MCU_WA_UNI_CMD(WSYS_CONFIG), &data, sizeof(data), true); return mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(WSYS_CONFIG), &data, sizeof(data), true); } int mt7996_mcu_fw_dbg_ctrl(struct mt7996_dev *dev, u32 module, u8 level) { struct { u8 _rsv[4]; __le16 tag; __le16 len; __le32 module_idx; u8 level; u8 _rsv2[3]; } data = { .tag = cpu_to_le16(UNI_WSYS_CONFIG_FW_DBG_CTRL), .len = cpu_to_le16(sizeof(data) - 4), .module_idx = cpu_to_le32(module), .level = level, }; return mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(WSYS_CONFIG), &data, sizeof(data), false); } static int mt7996_mcu_set_mwds(struct mt7996_dev *dev, bool enabled) { struct { u8 enable; u8 _rsv[3]; } __packed req = { .enable = enabled }; return mt76_mcu_send_msg(&dev->mt76, MCU_WA_EXT_CMD(MWDS_SUPPORT), &req, sizeof(req), false); } static void mt7996_add_rx_airtime_tlv(struct sk_buff *skb, u8 band_idx) { struct vow_rx_airtime *req; struct tlv *tlv; tlv = mt7996_mcu_add_uni_tlv(skb, UNI_VOW_RX_AT_AIRTIME_CLR_EN, sizeof(*req)); req = (struct vow_rx_airtime *)tlv; req->enable = true; req->band = band_idx; tlv = mt7996_mcu_add_uni_tlv(skb, UNI_VOW_RX_AT_AIRTIME_EN, sizeof(*req)); req = (struct vow_rx_airtime *)tlv; req->enable = true; req->band = band_idx; } static int mt7996_mcu_init_rx_airtime(struct mt7996_dev *dev) { struct uni_header hdr = {}; struct sk_buff *skb; int len, num, i; num = 2 + 2 * (mt7996_band_valid(dev, MT_BAND1) + mt7996_band_valid(dev, MT_BAND2)); len = sizeof(hdr) + num * sizeof(struct vow_rx_airtime); skb = mt76_mcu_msg_alloc(&dev->mt76, NULL, len); if (!skb) return -ENOMEM; skb_put_data(skb, &hdr, sizeof(hdr)); for (i = 0; i < __MT_MAX_BAND; i++) { if (mt7996_band_valid(dev, i)) mt7996_add_rx_airtime_tlv(skb, i); } return mt76_mcu_skb_send_msg(&dev->mt76, skb, MCU_WM_UNI_CMD(VOW), true); } int mt7996_mcu_init_firmware(struct mt7996_dev *dev) { int ret; /* force firmware operation mode into normal state, * which should be set before firmware download stage. */ mt76_wr(dev, MT_SWDEF_MODE, MT_SWDEF_NORMAL_MODE); ret = mt7996_driver_own(dev, 0); if (ret) return ret; /* set driver own for band1 when two hif exist */ if (dev->hif2) { ret = mt7996_driver_own(dev, 1); if (ret) return ret; } ret = mt7996_load_firmware(dev); if (ret) return ret; set_bit(MT76_STATE_MCU_RUNNING, &dev->mphy.state); ret = mt7996_mcu_fw_log_2_host(dev, MCU_FW_LOG_WM, 0); if (ret) return ret; if (mt7996_has_wa(dev)) { ret = mt7996_mcu_fw_log_2_host(dev, MCU_FW_LOG_WA, 0); if (ret) return ret; ret = mt7996_mcu_set_mwds(dev, 1); if (ret) return ret; } ret = mt7996_mcu_init_rx_airtime(dev); if (ret) return ret; return mt7996_mcu_wa_cmd(dev, MCU_WA_PARAM_CMD(SET), MCU_WA_PARAM_RED, 0, 0); } int mt7996_mcu_init(struct mt7996_dev *dev) { static const struct mt76_mcu_ops mt7996_mcu_ops = { .headroom = sizeof(struct mt76_connac2_mcu_txd), /* reuse */ .mcu_skb_send_msg = mt7996_mcu_send_message, .mcu_parse_response = mt7996_mcu_parse_response, }; dev->mt76.mcu_ops = &mt7996_mcu_ops; return mt7996_mcu_init_firmware(dev); } void mt7996_mcu_exit(struct mt7996_dev *dev) { mt7996_mcu_restart(&dev->mt76); if (mt7996_firmware_state(dev, FW_STATE_FW_DOWNLOAD)) { dev_err(dev->mt76.dev, "Failed to exit mcu\n"); goto out; } mt76_wr(dev, MT_TOP_LPCR_HOST_BAND(0), MT_TOP_LPCR_HOST_FW_OWN); if (dev->hif2) mt76_wr(dev, MT_TOP_LPCR_HOST_BAND(1), MT_TOP_LPCR_HOST_FW_OWN); out: skb_queue_purge(&dev->mt76.mcu.res_q); } int mt7996_mcu_set_hdr_trans(struct mt7996_dev *dev, bool hdr_trans) { struct { u8 __rsv[4]; } __packed hdr = {}; struct hdr_trans_blacklist *req_blacklist; struct hdr_trans_en *req_en; struct sk_buff *skb; struct tlv *tlv; int len = MT7996_HDR_TRANS_MAX_SIZE + sizeof(hdr); skb = mt76_mcu_msg_alloc(&dev->mt76, NULL, len); if (!skb) return -ENOMEM; skb_put_data(skb, &hdr, sizeof(hdr)); tlv = mt7996_mcu_add_uni_tlv(skb, UNI_HDR_TRANS_EN, sizeof(*req_en)); req_en = (struct hdr_trans_en *)tlv; req_en->enable = hdr_trans; tlv = mt7996_mcu_add_uni_tlv(skb, UNI_HDR_TRANS_VLAN, sizeof(struct hdr_trans_vlan)); if (hdr_trans) { tlv = mt7996_mcu_add_uni_tlv(skb, UNI_HDR_TRANS_BLACKLIST, sizeof(*req_blacklist)); req_blacklist = (struct hdr_trans_blacklist *)tlv; req_blacklist->enable = 1; req_blacklist->type = cpu_to_le16(ETH_P_PAE); } return mt76_mcu_skb_send_msg(&dev->mt76, skb, MCU_WM_UNI_CMD(RX_HDR_TRANS), true); } int mt7996_mcu_set_tx(struct mt7996_dev *dev, struct ieee80211_vif *vif, struct ieee80211_bss_conf *link_conf) { #define MCU_EDCA_AC_PARAM 0 #define WMM_AIFS_SET BIT(0) #define WMM_CW_MIN_SET BIT(1) #define WMM_CW_MAX_SET BIT(2) #define WMM_TXOP_SET BIT(3) #define WMM_PARAM_SET (WMM_AIFS_SET | WMM_CW_MIN_SET | \ WMM_CW_MAX_SET | WMM_TXOP_SET) struct mt7996_vif_link *link = mt7996_vif_conf_link(dev, vif, link_conf); struct { u8 bss_idx; u8 __rsv[3]; } __packed hdr = { .bss_idx = link->mt76.idx, }; struct sk_buff *skb; int len = sizeof(hdr) + IEEE80211_NUM_ACS * sizeof(struct edca); int ac; skb = mt76_mcu_msg_alloc(&dev->mt76, NULL, len); if (!skb) return -ENOMEM; skb_put_data(skb, &hdr, sizeof(hdr)); for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) { struct ieee80211_tx_queue_params *q = &link->queue_params[ac]; struct edca *e; struct tlv *tlv; tlv = mt7996_mcu_add_uni_tlv(skb, MCU_EDCA_AC_PARAM, sizeof(*e)); e = (struct edca *)tlv; e->set = WMM_PARAM_SET; e->queue = ac; e->aifs = q->aifs; e->txop = cpu_to_le16(q->txop); if (q->cw_min) e->cw_min = fls(q->cw_min); else e->cw_min = 5; if (q->cw_max) e->cw_max = fls(q->cw_max); else e->cw_max = 10; } return mt76_mcu_skb_send_msg(&dev->mt76, skb, MCU_WM_UNI_CMD(EDCA_UPDATE), true); } int mt7996_mcu_set_fcc5_lpn(struct mt7996_dev *dev, int val) { struct { u8 _rsv[4]; __le16 tag; __le16 len; __le32 ctrl; __le16 min_lpn; u8 rsv[2]; } __packed req = { .tag = cpu_to_le16(UNI_RDD_CTRL_SET_TH), .len = cpu_to_le16(sizeof(req) - 4), .ctrl = cpu_to_le32(0x1), .min_lpn = cpu_to_le16(val), }; return mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(RDD_CTRL), &req, sizeof(req), true); } int mt7996_mcu_set_pulse_th(struct mt7996_dev *dev, const struct mt7996_dfs_pulse *pulse) { struct { u8 _rsv[4]; __le16 tag; __le16 len; __le32 ctrl; __le32 max_width; /* us */ __le32 max_pwr; /* dbm */ __le32 min_pwr; /* dbm */ __le32 min_stgr_pri; /* us */ __le32 max_stgr_pri; /* us */ __le32 min_cr_pri; /* us */ __le32 max_cr_pri; /* us */ } __packed req = { .tag = cpu_to_le16(UNI_RDD_CTRL_SET_TH), .len = cpu_to_le16(sizeof(req) - 4), .ctrl = cpu_to_le32(0x3), #define __req_field(field) .field = cpu_to_le32(pulse->field) __req_field(max_width), __req_field(max_pwr), __req_field(min_pwr), __req_field(min_stgr_pri), __req_field(max_stgr_pri), __req_field(min_cr_pri), __req_field(max_cr_pri), #undef __req_field }; return mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(RDD_CTRL), &req, sizeof(req), true); } int mt7996_mcu_set_radar_th(struct mt7996_dev *dev, int index, const struct mt7996_dfs_pattern *pattern) { struct { u8 _rsv[4]; __le16 tag; __le16 len; __le32 ctrl; __le16 radar_type; u8 enb; u8 stgr; u8 min_crpn; u8 max_crpn; u8 min_crpr; u8 min_pw; __le32 min_pri; __le32 max_pri; u8 max_pw; u8 min_crbn; u8 max_crbn; u8 min_stgpn; u8 max_stgpn; u8 min_stgpr; u8 rsv[2]; __le32 min_stgpr_diff; } __packed req = { .tag = cpu_to_le16(UNI_RDD_CTRL_SET_TH), .len = cpu_to_le16(sizeof(req) - 4), .ctrl = cpu_to_le32(0x2), .radar_type = cpu_to_le16(index), #define __req_field_u8(field) .field = pattern->field #define __req_field_u32(field) .field = cpu_to_le32(pattern->field) __req_field_u8(enb), __req_field_u8(stgr), __req_field_u8(min_crpn), __req_field_u8(max_crpn), __req_field_u8(min_crpr), __req_field_u8(min_pw), __req_field_u32(min_pri), __req_field_u32(max_pri), __req_field_u8(max_pw), __req_field_u8(min_crbn), __req_field_u8(max_crbn), __req_field_u8(min_stgpn), __req_field_u8(max_stgpn), __req_field_u8(min_stgpr), __req_field_u32(min_stgpr_diff), #undef __req_field_u8 #undef __req_field_u32 }; return mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(RDD_CTRL), &req, sizeof(req), true); } static int mt7996_mcu_background_chain_ctrl(struct mt7996_phy *phy, struct cfg80211_chan_def *chandef, int cmd) { struct mt7996_dev *dev = phy->dev; struct mt76_phy *mphy = phy->mt76; struct ieee80211_channel *chan = mphy->chandef.chan; int freq = mphy->chandef.center_freq1; struct mt7996_mcu_background_chain_ctrl req = { .tag = cpu_to_le16(0), .len = cpu_to_le16(sizeof(req) - 4), .monitor_scan_type = 2, /* simple rx */ }; if (!chandef && cmd != CH_SWITCH_BACKGROUND_SCAN_STOP) return -EINVAL; if (!cfg80211_chandef_valid(&mphy->chandef)) return -EINVAL; switch (cmd) { case CH_SWITCH_BACKGROUND_SCAN_START: { req.chan = chan->hw_value; req.central_chan = ieee80211_frequency_to_channel(freq); req.bw = mt76_connac_chan_bw(&mphy->chandef); req.monitor_chan = chandef->chan->hw_value; req.monitor_central_chan = ieee80211_frequency_to_channel(chandef->center_freq1); req.monitor_bw = mt76_connac_chan_bw(chandef); req.band_idx = phy->mt76->band_idx; req.scan_mode = 1; break; } case CH_SWITCH_BACKGROUND_SCAN_RUNNING: req.monitor_chan = chandef->chan->hw_value; req.monitor_central_chan = ieee80211_frequency_to_channel(chandef->center_freq1); req.band_idx = phy->mt76->band_idx; req.scan_mode = 2; break; case CH_SWITCH_BACKGROUND_SCAN_STOP: req.chan = chan->hw_value; req.central_chan = ieee80211_frequency_to_channel(freq); req.bw = mt76_connac_chan_bw(&mphy->chandef); req.tx_stream = hweight8(mphy->antenna_mask); req.rx_stream = mphy->antenna_mask; break; default: return -EINVAL; } req.band = chandef ? chandef->chan->band == NL80211_BAND_5GHZ : 1; return mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(OFFCH_SCAN_CTRL), &req, sizeof(req), false); } int mt7996_mcu_rdd_background_enable(struct mt7996_phy *phy, struct cfg80211_chan_def *chandef) { struct mt7996_dev *dev = phy->dev; int err, region, rdd_idx = mt7996_get_rdd_idx(phy, true); if (!chandef) { /* disable offchain */ err = mt7996_mcu_rdd_cmd(dev, RDD_STOP, rdd_idx, 0); if (err) return err; return mt7996_mcu_background_chain_ctrl(phy, NULL, CH_SWITCH_BACKGROUND_SCAN_STOP); } err = mt7996_mcu_background_chain_ctrl(phy, chandef, CH_SWITCH_BACKGROUND_SCAN_START); if (err) return err; switch (dev->mt76.region) { case NL80211_DFS_ETSI: region = 0; break; case NL80211_DFS_JP: region = 2; break; case NL80211_DFS_FCC: default: region = 1; break; } return mt7996_mcu_rdd_cmd(dev, RDD_START, rdd_idx, region); } int mt7996_mcu_set_chan_info(struct mt7996_phy *phy, u16 tag) { static const u8 ch_band[] = { [NL80211_BAND_2GHZ] = 0, [NL80211_BAND_5GHZ] = 1, [NL80211_BAND_6GHZ] = 2, }; struct mt7996_dev *dev = phy->dev; struct cfg80211_chan_def *chandef = &phy->mt76->chandef; int freq1 = chandef->center_freq1; u8 band_idx = phy->mt76->band_idx; struct { /* fixed field */ u8 __rsv[4]; __le16 tag; __le16 len; u8 control_ch; u8 center_ch; u8 bw; u8 tx_path_num; u8 rx_path; /* mask or num */ u8 switch_reason; u8 band_idx; u8 center_ch2; /* for 80+80 only */ __le16 cac_case; u8 channel_band; u8 rsv0; __le32 outband_freq; u8 txpower_drop; u8 ap_bw; u8 ap_center_ch; u8 rsv1[53]; } __packed req = { .tag = cpu_to_le16(tag), .len = cpu_to_le16(sizeof(req) - 4), .control_ch = chandef->chan->hw_value, .center_ch = ieee80211_frequency_to_channel(freq1), .bw = mt76_connac_chan_bw(chandef), .tx_path_num = hweight16(phy->mt76->chainmask), .rx_path = mt7996_rx_chainmask(phy) >> dev->chainshift[band_idx], .band_idx = band_idx, .channel_band = ch_band[chandef->chan->band], }; if (phy->mt76->hw->conf.flags & IEEE80211_CONF_MONITOR) req.switch_reason = CH_SWITCH_NORMAL; else if (phy->mt76->offchannel || phy->mt76->hw->conf.flags & IEEE80211_CONF_IDLE) req.switch_reason = CH_SWITCH_SCAN_BYPASS_DPD; else if (!cfg80211_reg_can_beacon(phy->mt76->hw->wiphy, chandef, NL80211_IFTYPE_AP)) req.switch_reason = CH_SWITCH_DFS; else req.switch_reason = CH_SWITCH_NORMAL; if (tag == UNI_CHANNEL_SWITCH) req.rx_path = hweight8(req.rx_path); if (chandef->width == NL80211_CHAN_WIDTH_80P80) { int freq2 = chandef->center_freq2; req.center_ch2 = ieee80211_frequency_to_channel(freq2); } return mt76_mcu_send_msg(&dev->mt76, MCU_WMWA_UNI_CMD(CHANNEL_SWITCH), &req, sizeof(req), true); } static int mt7996_mcu_set_eeprom_flash(struct mt7996_dev *dev) { #define MAX_PAGE_IDX_MASK GENMASK(7, 5) #define PAGE_IDX_MASK GENMASK(4, 2) #define PER_PAGE_SIZE 0x400 struct mt7996_mcu_eeprom req = { .tag = cpu_to_le16(UNI_EFUSE_BUFFER_MODE), .buffer_mode = EE_MODE_BUFFER }; u16 eeprom_size = MT7996_EEPROM_SIZE; u8 total = DIV_ROUND_UP(eeprom_size, PER_PAGE_SIZE); u8 *eep = (u8 *)dev->mt76.eeprom.data; int eep_len, i; for (i = 0; i < total; i++, eep += eep_len) { struct sk_buff *skb; int ret, msg_len; if (i == total - 1 && !!(eeprom_size % PER_PAGE_SIZE)) eep_len = eeprom_size % PER_PAGE_SIZE; else eep_len = PER_PAGE_SIZE; msg_len = sizeof(req) + eep_len; skb = mt76_mcu_msg_alloc(&dev->mt76, NULL, msg_len); if (!skb) return -ENOMEM; req.len = cpu_to_le16(msg_len - 4); req.format = FIELD_PREP(MAX_PAGE_IDX_MASK, total - 1) | FIELD_PREP(PAGE_IDX_MASK, i) | EE_FORMAT_WHOLE; req.buf_len = cpu_to_le16(eep_len); skb_put_data(skb, &req, sizeof(req)); skb_put_data(skb, eep, eep_len); ret = mt76_mcu_skb_send_msg(&dev->mt76, skb, MCU_WM_UNI_CMD(EFUSE_CTRL), true); if (ret) return ret; } return 0; } int mt7996_mcu_set_eeprom(struct mt7996_dev *dev) { struct mt7996_mcu_eeprom req = { .tag = cpu_to_le16(UNI_EFUSE_BUFFER_MODE), .len = cpu_to_le16(sizeof(req) - 4), .buffer_mode = EE_MODE_EFUSE, .format = EE_FORMAT_WHOLE }; if (dev->flash_mode) return mt7996_mcu_set_eeprom_flash(dev); return mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(EFUSE_CTRL), &req, sizeof(req), true); } int mt7996_mcu_get_eeprom(struct mt7996_dev *dev, u32 offset, u8 *buf, u32 buf_len) { struct { u8 _rsv[4]; __le16 tag; __le16 len; __le32 addr; __le32 valid; u8 data[16]; } __packed req = { .tag = cpu_to_le16(UNI_EFUSE_ACCESS), .len = cpu_to_le16(sizeof(req) - 4), .addr = cpu_to_le32(round_down(offset, MT7996_EEPROM_BLOCK_SIZE)), }; struct sk_buff *skb; bool valid; int ret; ret = mt76_mcu_send_and_get_msg(&dev->mt76, MCU_WM_UNI_CMD_QUERY(EFUSE_CTRL), &req, sizeof(req), true, &skb); if (ret) return ret; valid = le32_to_cpu(*(__le32 *)(skb->data + 16)); if (valid) { u32 addr = le32_to_cpu(*(__le32 *)(skb->data + 12)); if (!buf) buf = (u8 *)dev->mt76.eeprom.data + addr; if (!buf_len || buf_len > MT7996_EEPROM_BLOCK_SIZE) buf_len = MT7996_EEPROM_BLOCK_SIZE; skb_pull(skb, 48); memcpy(buf, skb->data, buf_len); } else { ret = -EINVAL; } dev_kfree_skb(skb); return ret; } int mt7996_mcu_get_eeprom_free_block(struct mt7996_dev *dev, u8 *block_num) { struct { u8 _rsv[4]; __le16 tag; __le16 len; u8 num; u8 version; u8 die_idx; u8 _rsv2; } __packed req = { .tag = cpu_to_le16(UNI_EFUSE_FREE_BLOCK), .len = cpu_to_le16(sizeof(req) - 4), .version = 2, }; struct sk_buff *skb; int ret; ret = mt76_mcu_send_and_get_msg(&dev->mt76, MCU_WM_UNI_CMD_QUERY(EFUSE_CTRL), &req, sizeof(req), true, &skb); if (ret) return ret; *block_num = *(u8 *)(skb->data + 8); dev_kfree_skb(skb); return 0; } int mt7996_mcu_get_chip_config(struct mt7996_dev *dev, u32 *cap) { #define NIC_CAP 3 #define UNI_EVENT_CHIP_CONFIG_EFUSE_VERSION 0x21 struct { u8 _rsv[4]; __le16 tag; __le16 len; } __packed req = { .tag = cpu_to_le16(NIC_CAP), .len = cpu_to_le16(sizeof(req) - 4), }; struct sk_buff *skb; u8 *buf; int ret; ret = mt76_mcu_send_and_get_msg(&dev->mt76, MCU_WM_UNI_CMD_QUERY(CHIP_CONFIG), &req, sizeof(req), true, &skb); if (ret) return ret; /* fixed field */ skb_pull(skb, 4); buf = skb->data; while (buf - skb->data < skb->len) { struct tlv *tlv = (struct tlv *)buf; switch (le16_to_cpu(tlv->tag)) { case UNI_EVENT_CHIP_CONFIG_EFUSE_VERSION: *cap = le32_to_cpu(*(__le32 *)(buf + sizeof(*tlv))); break; default: break; } buf += le16_to_cpu(tlv->len); } dev_kfree_skb(skb); return 0; } int mt7996_mcu_get_chan_mib_info(struct mt7996_phy *phy, bool chan_switch) { enum { IDX_TX_TIME, IDX_RX_TIME, IDX_OBSS_AIRTIME, IDX_NON_WIFI_TIME, IDX_NUM }; struct { struct { u8 band; u8 __rsv[3]; } hdr; struct { __le16 tag; __le16 len; __le32 offs; } data[IDX_NUM]; } __packed req = { .hdr.band = phy->mt76->band_idx, }; static const u32 offs[] = { [IDX_TX_TIME] = UNI_MIB_TX_TIME, [IDX_RX_TIME] = UNI_MIB_RX_TIME, [IDX_OBSS_AIRTIME] = UNI_MIB_OBSS_AIRTIME, [IDX_NON_WIFI_TIME] = UNI_MIB_NON_WIFI_TIME, }; struct mt76_channel_state *state = phy->mt76->chan_state; struct mt76_channel_state *state_ts = &phy->state_ts; struct mt7996_dev *dev = phy->dev; struct mt7996_mcu_mib *res; struct sk_buff *skb; int i, ret; for (i = 0; i < IDX_NUM; i++) { req.data[i].tag = cpu_to_le16(UNI_CMD_MIB_DATA); req.data[i].len = cpu_to_le16(sizeof(req.data[i])); req.data[i].offs = cpu_to_le32(offs[i]); } ret = mt76_mcu_send_and_get_msg(&dev->mt76, MCU_WM_UNI_CMD_QUERY(GET_MIB_INFO), &req, sizeof(req), true, &skb); if (ret) return ret; skb_pull(skb, sizeof(req.hdr)); res = (struct mt7996_mcu_mib *)(skb->data); if (chan_switch) goto out; #define __res_u64(s) le64_to_cpu(res[s].data) state->cc_tx += __res_u64(IDX_TX_TIME) - state_ts->cc_tx; state->cc_bss_rx += __res_u64(IDX_RX_TIME) - state_ts->cc_bss_rx; state->cc_rx += __res_u64(IDX_RX_TIME) + __res_u64(IDX_OBSS_AIRTIME) - state_ts->cc_rx; state->cc_busy += __res_u64(IDX_TX_TIME) + __res_u64(IDX_RX_TIME) + __res_u64(IDX_OBSS_AIRTIME) + __res_u64(IDX_NON_WIFI_TIME) - state_ts->cc_busy; out: state_ts->cc_tx = __res_u64(IDX_TX_TIME); state_ts->cc_bss_rx = __res_u64(IDX_RX_TIME); state_ts->cc_rx = __res_u64(IDX_RX_TIME) + __res_u64(IDX_OBSS_AIRTIME); state_ts->cc_busy = __res_u64(IDX_TX_TIME) + __res_u64(IDX_RX_TIME) + __res_u64(IDX_OBSS_AIRTIME) + __res_u64(IDX_NON_WIFI_TIME); #undef __res_u64 dev_kfree_skb(skb); return 0; } int mt7996_mcu_get_temperature(struct mt7996_phy *phy) { #define TEMPERATURE_QUERY 0 #define GET_TEMPERATURE 0 struct { u8 _rsv[4]; __le16 tag; __le16 len; u8 rsv1; u8 action; u8 band_idx; u8 rsv2; } req = { .tag = cpu_to_le16(TEMPERATURE_QUERY), .len = cpu_to_le16(sizeof(req) - 4), .action = GET_TEMPERATURE, .band_idx = phy->mt76->band_idx, }; struct mt7996_mcu_thermal { u8 _rsv[4]; __le16 tag; __le16 len; __le32 rsv; __le32 temperature; } __packed * res; struct sk_buff *skb; int ret; u32 temp; ret = mt76_mcu_send_and_get_msg(&phy->dev->mt76, MCU_WM_UNI_CMD(THERMAL), &req, sizeof(req), true, &skb); if (ret) return ret; res = (void *)skb->data; temp = le32_to_cpu(res->temperature); dev_kfree_skb(skb); return temp; } int mt7996_mcu_set_thermal_throttling(struct mt7996_phy *phy, u8 state) { struct { u8 _rsv[4]; __le16 tag; __le16 len; struct mt7996_mcu_thermal_ctrl ctrl; } __packed req = { .tag = cpu_to_le16(UNI_CMD_THERMAL_PROTECT_DUTY_CONFIG), .len = cpu_to_le16(sizeof(req) - 4), .ctrl = { .band_idx = phy->mt76->band_idx, }, }; int level, ret; /* set duty cycle and level */ for (level = 0; level < 4; level++) { req.ctrl.duty.duty_level = level; req.ctrl.duty.duty_cycle = state; state /= 2; ret = mt76_mcu_send_msg(&phy->dev->mt76, MCU_WM_UNI_CMD(THERMAL), &req, sizeof(req), false); if (ret) return ret; } return 0; } int mt7996_mcu_set_thermal_protect(struct mt7996_phy *phy, bool enable) { #define SUSTAIN_PERIOD 10 struct { u8 _rsv[4]; __le16 tag; __le16 len; struct mt7996_mcu_thermal_ctrl ctrl; struct mt7996_mcu_thermal_enable enable; } __packed req = { .len = cpu_to_le16(sizeof(req) - 4 - sizeof(req.enable)), .ctrl = { .band_idx = phy->mt76->band_idx, .type.protect_type = 1, .type.trigger_type = 1, }, }; int ret; req.tag = cpu_to_le16(UNI_CMD_THERMAL_PROTECT_DISABLE); ret = mt76_mcu_send_msg(&phy->dev->mt76, MCU_WM_UNI_CMD(THERMAL), &req, sizeof(req) - sizeof(req.enable), false); if (ret || !enable) return ret; /* set high-temperature trigger threshold */ req.tag = cpu_to_le16(UNI_CMD_THERMAL_PROTECT_ENABLE); req.enable.restore_temp = cpu_to_le32(phy->throttle_temp[0]); req.enable.trigger_temp = cpu_to_le32(phy->throttle_temp[1]); req.enable.sustain_time = cpu_to_le16(SUSTAIN_PERIOD); req.len = cpu_to_le16(sizeof(req) - 4); return mt76_mcu_send_msg(&phy->dev->mt76, MCU_WM_UNI_CMD(THERMAL), &req, sizeof(req), false); } int mt7996_mcu_set_ser(struct mt7996_dev *dev, u8 action, u8 val, u8 band) { struct { u8 rsv[4]; __le16 tag; __le16 len; union { struct { __le32 mask; } __packed set; struct { u8 method; u8 band; u8 rsv2[2]; } __packed trigger; }; } __packed req = { .tag = cpu_to_le16(action), .len = cpu_to_le16(sizeof(req) - 4), }; switch (action) { case UNI_CMD_SER_SET: req.set.mask = cpu_to_le32(val); break; case UNI_CMD_SER_TRIGGER: req.trigger.method = val; req.trigger.band = band; break; default: return -EINVAL; } return mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(SER), &req, sizeof(req), false); } int mt7996_mcu_set_txbf(struct mt7996_dev *dev, u8 action) { #define MT7996_BF_MAX_SIZE sizeof(union bf_tag_tlv) #define BF_PROCESSING 4 struct uni_header hdr; struct sk_buff *skb; struct tlv *tlv; int len = sizeof(hdr) + MT7996_BF_MAX_SIZE; memset(&hdr, 0, sizeof(hdr)); skb = mt76_mcu_msg_alloc(&dev->mt76, NULL, len); if (!skb) return -ENOMEM; skb_put_data(skb, &hdr, sizeof(hdr)); switch (action) { case BF_SOUNDING_ON: { struct bf_sounding_on *req_snd_on; tlv = mt7996_mcu_add_uni_tlv(skb, action, sizeof(*req_snd_on)); req_snd_on = (struct bf_sounding_on *)tlv; req_snd_on->snd_mode = BF_PROCESSING; break; } case BF_HW_EN_UPDATE: { struct bf_hw_en_status_update *req_hw_en; tlv = mt7996_mcu_add_uni_tlv(skb, action, sizeof(*req_hw_en)); req_hw_en = (struct bf_hw_en_status_update *)tlv; req_hw_en->ebf = true; req_hw_en->ibf = dev->ibf; break; } case BF_MOD_EN_CTRL: { struct bf_mod_en_ctrl *req_mod_en; tlv = mt7996_mcu_add_uni_tlv(skb, action, sizeof(*req_mod_en)); req_mod_en = (struct bf_mod_en_ctrl *)tlv; req_mod_en->bf_num = mt7996_band_valid(dev, MT_BAND2) ? 3 : 2; req_mod_en->bf_bitmap = mt7996_band_valid(dev, MT_BAND2) ? GENMASK(2, 0) : GENMASK(1, 0); break; } default: return -EINVAL; } return mt76_mcu_skb_send_msg(&dev->mt76, skb, MCU_WM_UNI_CMD(BF), true); } static int mt7996_mcu_enable_obss_spr(struct mt7996_phy *phy, u16 action, u8 val) { struct mt7996_dev *dev = phy->dev; struct { u8 band_idx; u8 __rsv[3]; __le16 tag; __le16 len; __le32 val; } __packed req = { .band_idx = phy->mt76->band_idx, .tag = cpu_to_le16(action), .len = cpu_to_le16(sizeof(req) - 4), .val = cpu_to_le32(val), }; return mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(SR), &req, sizeof(req), true); } static int mt7996_mcu_set_obss_spr_pd(struct mt7996_phy *phy, struct ieee80211_he_obss_pd *he_obss_pd) { struct mt7996_dev *dev = phy->dev; u8 max_th = 82, non_srg_max_th = 62; struct { u8 band_idx; u8 __rsv[3]; __le16 tag; __le16 len; u8 pd_th_non_srg; u8 pd_th_srg; u8 period_offs; u8 rcpi_src; __le16 obss_pd_min; __le16 obss_pd_min_srg; u8 resp_txpwr_mode; u8 txpwr_restrict_mode; u8 txpwr_ref; u8 __rsv2[3]; } __packed req = { .band_idx = phy->mt76->band_idx, .tag = cpu_to_le16(UNI_CMD_SR_SET_PARAM), .len = cpu_to_le16(sizeof(req) - 4), .obss_pd_min = cpu_to_le16(max_th), .obss_pd_min_srg = cpu_to_le16(max_th), .txpwr_restrict_mode = 2, .txpwr_ref = 21 }; int ret; /* disable firmware dynamical PD asjustment */ ret = mt7996_mcu_enable_obss_spr(phy, UNI_CMD_SR_ENABLE_DPD, false); if (ret) return ret; if (he_obss_pd->sr_ctrl & IEEE80211_HE_SPR_NON_SRG_OBSS_PD_SR_DISALLOWED) req.pd_th_non_srg = max_th; else if (he_obss_pd->sr_ctrl & IEEE80211_HE_SPR_NON_SRG_OFFSET_PRESENT) req.pd_th_non_srg = max_th - he_obss_pd->non_srg_max_offset; else req.pd_th_non_srg = non_srg_max_th; if (he_obss_pd->sr_ctrl & IEEE80211_HE_SPR_SRG_INFORMATION_PRESENT) req.pd_th_srg = max_th - he_obss_pd->max_offset; return mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(SR), &req, sizeof(req), true); } static int mt7996_mcu_set_obss_spr_siga(struct mt7996_phy *phy, struct mt7996_vif_link *link, struct ieee80211_he_obss_pd *he_obss_pd) { struct mt7996_dev *dev = phy->dev; u8 omac = link->mt76.omac_idx; struct { u8 band_idx; u8 __rsv[3]; __le16 tag; __le16 len; u8 omac; u8 __rsv2[3]; u8 flag[20]; } __packed req = { .band_idx = phy->mt76->band_idx, .tag = cpu_to_le16(UNI_CMD_SR_SET_SIGA), .len = cpu_to_le16(sizeof(req) - 4), .omac = omac > HW_BSSID_MAX ? omac - 12 : omac, }; int ret; if (he_obss_pd->sr_ctrl & IEEE80211_HE_SPR_HESIGA_SR_VAL15_ALLOWED) req.flag[req.omac] = 0xf; else return 0; /* switch to normal AP mode */ ret = mt7996_mcu_enable_obss_spr(phy, UNI_CMD_SR_ENABLE_MODE, 0); if (ret) return ret; return mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(SR), &req, sizeof(req), true); } static int mt7996_mcu_set_obss_spr_bitmap(struct mt7996_phy *phy, struct ieee80211_he_obss_pd *he_obss_pd) { struct mt7996_dev *dev = phy->dev; struct { u8 band_idx; u8 __rsv[3]; __le16 tag; __le16 len; __le32 color_l[2]; __le32 color_h[2]; __le32 bssid_l[2]; __le32 bssid_h[2]; } __packed req = { .band_idx = phy->mt76->band_idx, .tag = cpu_to_le16(UNI_CMD_SR_SET_SRG_BITMAP), .len = cpu_to_le16(sizeof(req) - 4), }; u32 bitmap; memcpy(&bitmap, he_obss_pd->bss_color_bitmap, sizeof(bitmap)); req.color_l[req.band_idx] = cpu_to_le32(bitmap); memcpy(&bitmap, he_obss_pd->bss_color_bitmap + 4, sizeof(bitmap)); req.color_h[req.band_idx] = cpu_to_le32(bitmap); memcpy(&bitmap, he_obss_pd->partial_bssid_bitmap, sizeof(bitmap)); req.bssid_l[req.band_idx] = cpu_to_le32(bitmap); memcpy(&bitmap, he_obss_pd->partial_bssid_bitmap + 4, sizeof(bitmap)); req.bssid_h[req.band_idx] = cpu_to_le32(bitmap); return mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(SR), &req, sizeof(req), true); } int mt7996_mcu_add_obss_spr(struct mt7996_phy *phy, struct mt7996_vif_link *link, struct ieee80211_he_obss_pd *he_obss_pd) { int ret; /* enable firmware scene detection algorithms */ ret = mt7996_mcu_enable_obss_spr(phy, UNI_CMD_SR_ENABLE_SD, sr_scene_detect); if (ret) return ret; /* firmware dynamically adjusts PD threshold so skip manual control */ if (sr_scene_detect && !he_obss_pd->enable) return 0; /* enable spatial reuse */ ret = mt7996_mcu_enable_obss_spr(phy, UNI_CMD_SR_ENABLE, he_obss_pd->enable); if (ret) return ret; if (sr_scene_detect || !he_obss_pd->enable) return 0; ret = mt7996_mcu_enable_obss_spr(phy, UNI_CMD_SR_ENABLE_TX, true); if (ret) return ret; /* set SRG/non-SRG OBSS PD threshold */ ret = mt7996_mcu_set_obss_spr_pd(phy, he_obss_pd); if (ret) return ret; /* Set SR prohibit */ ret = mt7996_mcu_set_obss_spr_siga(phy, link, he_obss_pd); if (ret) return ret; /* set SRG BSS color/BSSID bitmap */ return mt7996_mcu_set_obss_spr_bitmap(phy, he_obss_pd); } int mt7996_mcu_update_bss_color(struct mt7996_dev *dev, struct mt76_vif_link *mlink, struct cfg80211_he_bss_color *he_bss_color) { int len = sizeof(struct bss_req_hdr) + sizeof(struct bss_color_tlv); struct bss_color_tlv *bss_color; struct sk_buff *skb; struct tlv *tlv; skb = __mt7996_mcu_alloc_bss_req(&dev->mt76, mlink, len); if (IS_ERR(skb)) return PTR_ERR(skb); tlv = mt76_connac_mcu_add_tlv(skb, UNI_BSS_INFO_BSS_COLOR, sizeof(*bss_color)); bss_color = (struct bss_color_tlv *)tlv; bss_color->enable = he_bss_color->enabled; bss_color->color = he_bss_color->color; return mt76_mcu_skb_send_msg(&dev->mt76, skb, MCU_WMWA_UNI_CMD(BSS_INFO_UPDATE), true); } #define TWT_AGRT_TRIGGER BIT(0) #define TWT_AGRT_ANNOUNCE BIT(1) #define TWT_AGRT_PROTECT BIT(2) int mt7996_mcu_twt_agrt_update(struct mt7996_dev *dev, struct mt7996_vif_link *link, struct mt7996_twt_flow *flow, int cmd) { struct { /* fixed field */ u8 bss; u8 _rsv[3]; __le16 tag; __le16 len; u8 tbl_idx; u8 cmd; u8 own_mac_idx; u8 flowid; /* 0xff for group id */ __le16 peer_id; /* specify the peer_id (msb=0) * or group_id (msb=1) */ u8 duration; /* 256 us */ u8 bss_idx; __le64 start_tsf; __le16 mantissa; u8 exponent; u8 is_ap; u8 agrt_params; u8 __rsv2[23]; } __packed req = { .tag = cpu_to_le16(UNI_CMD_TWT_ARGT_UPDATE), .len = cpu_to_le16(sizeof(req) - 4), .tbl_idx = flow->table_id, .cmd = cmd, .own_mac_idx = link->mt76.omac_idx, .flowid = flow->id, .peer_id = cpu_to_le16(flow->wcid), .duration = flow->duration, .bss = link->mt76.idx, .bss_idx = link->mt76.idx, .start_tsf = cpu_to_le64(flow->tsf), .mantissa = flow->mantissa, .exponent = flow->exp, .is_ap = true, }; if (flow->protection) req.agrt_params |= TWT_AGRT_PROTECT; if (!flow->flowtype) req.agrt_params |= TWT_AGRT_ANNOUNCE; if (flow->trigger) req.agrt_params |= TWT_AGRT_TRIGGER; return mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(TWT), &req, sizeof(req), true); } int mt7996_mcu_set_rts_thresh(struct mt7996_phy *phy, u32 val) { struct { u8 band_idx; u8 _rsv[3]; __le16 tag; __le16 len; __le32 len_thresh; __le32 pkt_thresh; } __packed req = { .band_idx = phy->mt76->band_idx, .tag = cpu_to_le16(UNI_BAND_CONFIG_RTS_THRESHOLD), .len = cpu_to_le16(sizeof(req) - 4), .len_thresh = cpu_to_le32(val), .pkt_thresh = cpu_to_le32(0x2), }; return mt76_mcu_send_msg(&phy->dev->mt76, MCU_WM_UNI_CMD(BAND_CONFIG), &req, sizeof(req), true); } int mt7996_mcu_set_radio_en(struct mt7996_phy *phy, bool enable) { struct { u8 band_idx; u8 _rsv[3]; __le16 tag; __le16 len; u8 enable; u8 _rsv2[3]; } __packed req = { .band_idx = phy->mt76->band_idx, .tag = cpu_to_le16(UNI_BAND_CONFIG_RADIO_ENABLE), .len = cpu_to_le16(sizeof(req) - 4), .enable = enable, }; return mt76_mcu_send_msg(&phy->dev->mt76, MCU_WM_UNI_CMD(BAND_CONFIG), &req, sizeof(req), true); } int mt7996_mcu_rdd_cmd(struct mt7996_dev *dev, int cmd, u8 rdd_idx, u8 val) { struct { u8 _rsv[4]; __le16 tag; __le16 len; u8 ctrl; u8 rdd_idx; u8 rdd_rx_sel; u8 val; u8 rsv[4]; } __packed req = { .tag = cpu_to_le16(UNI_RDD_CTRL_PARM), .len = cpu_to_le16(sizeof(req) - 4), .ctrl = cmd, .rdd_idx = rdd_idx, .val = val, }; return mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(RDD_CTRL), &req, sizeof(req), true); } int mt7996_mcu_wtbl_update_hdr_trans(struct mt7996_dev *dev, struct ieee80211_vif *vif, struct mt7996_vif_link *link, struct mt7996_sta_link *msta_link) { struct sk_buff *skb; skb = __mt76_connac_mcu_alloc_sta_req(&dev->mt76, &link->mt76, &msta_link->wcid, MT7996_STA_UPDATE_MAX_SIZE); if (IS_ERR(skb)) return PTR_ERR(skb); /* starec hdr trans */ mt7996_mcu_sta_hdr_trans_tlv(dev, skb, vif, &msta_link->wcid); return mt76_mcu_skb_send_msg(&dev->mt76, skb, MCU_WMWA_UNI_CMD(STA_REC_UPDATE), true); } int mt7996_mcu_set_fixed_rate_table(struct mt7996_phy *phy, u8 table_idx, u16 rate_idx, bool beacon) { #define UNI_FIXED_RATE_TABLE_SET 0 #define SPE_IXD_SELECT_TXD 0 #define SPE_IXD_SELECT_BMC_WTBL 1 struct mt7996_dev *dev = phy->dev; struct fixed_rate_table_ctrl req = { .tag = cpu_to_le16(UNI_FIXED_RATE_TABLE_SET), .len = cpu_to_le16(sizeof(req) - 4), .table_idx = table_idx, .rate_idx = cpu_to_le16(rate_idx), .gi = 1, .he_ltf = 1, }; u8 band_idx = phy->mt76->band_idx; if (beacon) { req.spe_idx_sel = SPE_IXD_SELECT_TXD; req.spe_idx = 24 + band_idx; phy->beacon_rate = rate_idx; } else { req.spe_idx_sel = SPE_IXD_SELECT_BMC_WTBL; } return mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(FIXED_RATE_TABLE), &req, sizeof(req), false); } int mt7996_mcu_rf_regval(struct mt7996_dev *dev, u32 regidx, u32 *val, bool set) { struct { u8 __rsv1[4]; __le16 tag; __le16 len; __le16 idx; u8 __rsv2[2]; __le32 ofs; __le32 data; } __packed *res, req = { .tag = cpu_to_le16(UNI_CMD_ACCESS_RF_REG_BASIC), .len = cpu_to_le16(sizeof(req) - 4), .idx = cpu_to_le16(u32_get_bits(regidx, GENMASK(31, 24))), .ofs = cpu_to_le32(u32_get_bits(regidx, GENMASK(23, 0))), .data = set ? cpu_to_le32(*val) : 0, }; struct sk_buff *skb; int ret; if (set) return mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(REG_ACCESS), &req, sizeof(req), true); ret = mt76_mcu_send_and_get_msg(&dev->mt76, MCU_WM_UNI_CMD_QUERY(REG_ACCESS), &req, sizeof(req), true, &skb); if (ret) return ret; res = (void *)skb->data; *val = le32_to_cpu(res->data); dev_kfree_skb(skb); return 0; } int mt7996_mcu_trigger_assert(struct mt7996_dev *dev) { struct { __le16 tag; __le16 len; u8 enable; u8 rsv[3]; } __packed req = { .len = cpu_to_le16(sizeof(req) - 4), .enable = true, }; return mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(ASSERT_DUMP), &req, sizeof(req), false); } int mt7996_mcu_set_rro(struct mt7996_dev *dev, u16 tag, u16 val) { struct { u8 __rsv1[4]; __le16 tag; __le16 len; union { struct { u8 type; u8 __rsv2[3]; } __packed platform_type; struct { u8 type; u8 dest; u8 __rsv2[2]; } __packed bypass_mode; struct { u8 path; u8 __rsv2[3]; } __packed txfree_path; struct { __le16 flush_one; __le16 flush_all; u8 __rsv2[4]; } __packed timeout; }; } __packed req = { .tag = cpu_to_le16(tag), .len = cpu_to_le16(sizeof(req) - 4), }; switch (tag) { case UNI_RRO_SET_PLATFORM_TYPE: req.platform_type.type = val; break; case UNI_RRO_SET_BYPASS_MODE: req.bypass_mode.type = val; break; case UNI_RRO_SET_TXFREE_PATH: req.txfree_path.path = val; break; case UNI_RRO_SET_FLUSH_TIMEOUT: req.timeout.flush_one = cpu_to_le16(val); req.timeout.flush_all = cpu_to_le16(2 * val); break; default: return -EINVAL; } return mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(RRO), &req, sizeof(req), true); } int mt7996_mcu_get_all_sta_info(struct mt7996_phy *phy, u16 tag) { struct mt7996_dev *dev = phy->dev; struct { u8 _rsv[4]; __le16 tag; __le16 len; } __packed req = { .tag = cpu_to_le16(tag), .len = cpu_to_le16(sizeof(req) - 4), }; return mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(ALL_STA_INFO), &req, sizeof(req), false); } int mt7996_mcu_wed_rro_reset_sessions(struct mt7996_dev *dev, u16 id) { struct { u8 __rsv[4]; __le16 tag; __le16 len; __le16 session_id; u8 pad[4]; } __packed req = { .tag = cpu_to_le16(UNI_RRO_DEL_BA_SESSION), .len = cpu_to_le16(sizeof(req) - 4), .session_id = cpu_to_le16(id), }; return mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(RRO), &req, sizeof(req), true); } int mt7996_mcu_set_sniffer_mode(struct mt7996_phy *phy, bool enabled) { struct mt7996_dev *dev = phy->dev; struct { u8 band_idx; u8 _rsv[3]; __le16 tag; __le16 len; u8 enable; u8 _pad[3]; } __packed req = { .band_idx = phy->mt76->band_idx, .tag = 0, .len = cpu_to_le16(sizeof(req) - 4), .enable = enabled, }; return mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(SNIFFER), &req, sizeof(req), true); } int mt7996_mcu_set_txpower_sku(struct mt7996_phy *phy) { #define TX_POWER_LIMIT_TABLE_RATE 0 struct mt7996_dev *dev = phy->dev; struct mt76_phy *mphy = phy->mt76; struct tx_power_limit_table_ctrl { u8 __rsv1[4]; __le16 tag; __le16 len; u8 power_ctrl_id; u8 power_limit_type; u8 band_idx; } __packed req = { .tag = cpu_to_le16(UNI_TXPOWER_POWER_LIMIT_TABLE_CTRL), .len = cpu_to_le16(sizeof(req) + MT7996_SKU_PATH_NUM - 4), .power_ctrl_id = UNI_TXPOWER_POWER_LIMIT_TABLE_CTRL, .power_limit_type = TX_POWER_LIMIT_TABLE_RATE, .band_idx = phy->mt76->band_idx, }; struct mt76_power_limits la = {}; struct sk_buff *skb; int i, tx_power; tx_power = mt76_get_power_bound(mphy, phy->txpower); tx_power = mt76_get_rate_power_limits(mphy, mphy->chandef.chan, &la, tx_power); mphy->txpower_cur = tx_power; skb = mt76_mcu_msg_alloc(&dev->mt76, NULL, sizeof(req) + MT7996_SKU_PATH_NUM); if (!skb) return -ENOMEM; skb_put_data(skb, &req, sizeof(req)); /* cck and ofdm */ skb_put_data(skb, &la.cck, sizeof(la.cck)); skb_put_data(skb, &la.ofdm, sizeof(la.ofdm)); /* ht20 */ skb_put_data(skb, &la.mcs[0], 8); /* ht40 */ skb_put_data(skb, &la.mcs[1], 9); /* vht */ for (i = 0; i < 4; i++) { skb_put_data(skb, &la.mcs[i], sizeof(la.mcs[i])); skb_put_zero(skb, 2); /* padding */ } /* he */ skb_put_data(skb, &la.ru[0], sizeof(la.ru)); /* eht */ skb_put_data(skb, &la.eht[0], sizeof(la.eht)); /* padding */ skb_put_zero(skb, MT7996_SKU_PATH_NUM - MT7996_SKU_RATE_NUM); return mt76_mcu_skb_send_msg(&dev->mt76, skb, MCU_WM_UNI_CMD(TXPOWER), true); } int mt7996_mcu_cp_support(struct mt7996_dev *dev, u8 mode) { __le32 cp_mode; if (mode < mt76_connac_lmac_mapping(IEEE80211_AC_BE) || mode > mt76_connac_lmac_mapping(IEEE80211_AC_VO)) return -EINVAL; if (!mt7996_has_wa(dev)) { struct { u8 _rsv[4]; __le16 tag; __le16 len; u8 cp_mode; u8 rsv[3]; } __packed req = { .tag = cpu_to_le16(UNI_CMD_SDO_CP_MODE), .len = cpu_to_le16(sizeof(req) - 4), .cp_mode = mode, }; return mt76_mcu_send_msg(&dev->mt76, MCU_WA_UNI_CMD(SDO), &req, sizeof(req), false); } cp_mode = cpu_to_le32(mode); return mt76_mcu_send_msg(&dev->mt76, MCU_WA_EXT_CMD(CP_SUPPORT), &cp_mode, sizeof(cp_mode), true); } diff --git a/sys/contrib/dev/mediatek/mt76/mt7996/mmio.c b/sys/contrib/dev/mediatek/mt76/mt7996/mmio.c index bb82f5807ff8..05a3d9019167 100644 --- a/sys/contrib/dev/mediatek/mt76/mt7996/mmio.c +++ b/sys/contrib/dev/mediatek/mt76/mt7996/mmio.c @@ -1,850 +1,854 @@ // SPDX-License-Identifier: ISC /* * Copyright (C) 2022 MediaTek Inc. */ +#if defined(__FreeBSD__) +#define LINUXKPI_PARAM_PREFIX mt7996_ +#endif + #include #include #include #include #include "mt7996.h" #include "mac.h" #include "mcu.h" #include "../trace.h" #include "../dma.h" static bool wed_enable; module_param(wed_enable, bool, 0644); #if defined(__FreeBSD__) MODULE_PARM_DESC(wed_enable, "Enable Wireless Ethernet Dispatch support"); #endif static const struct __base mt7996_reg_base[] = { [WF_AGG_BASE] = { { 0x820e2000, 0x820f2000, 0x830e2000 } }, [WF_ARB_BASE] = { { 0x820e3000, 0x820f3000, 0x830e3000 } }, [WF_TMAC_BASE] = { { 0x820e4000, 0x820f4000, 0x830e4000 } }, [WF_RMAC_BASE] = { { 0x820e5000, 0x820f5000, 0x830e5000 } }, [WF_DMA_BASE] = { { 0x820e7000, 0x820f7000, 0x830e7000 } }, [WF_WTBLOFF_BASE] = { { 0x820e9000, 0x820f9000, 0x830e9000 } }, [WF_ETBF_BASE] = { { 0x820ea000, 0x820fa000, 0x830ea000 } }, [WF_LPON_BASE] = { { 0x820eb000, 0x820fb000, 0x830eb000 } }, [WF_MIB_BASE] = { { 0x820ed000, 0x820fd000, 0x830ed000 } }, [WF_RATE_BASE] = { { 0x820ee000, 0x820fe000, 0x830ee000 } }, }; static const u32 mt7996_offs[] = { [MIB_RVSR0] = 0x720, [MIB_RVSR1] = 0x724, [MIB_BTSCR5] = 0x788, [MIB_BTSCR6] = 0x798, [MIB_RSCR1] = 0x7ac, [MIB_RSCR27] = 0x954, [MIB_RSCR28] = 0x958, [MIB_RSCR29] = 0x95c, [MIB_RSCR30] = 0x960, [MIB_RSCR31] = 0x964, [MIB_RSCR33] = 0x96c, [MIB_RSCR35] = 0x974, [MIB_RSCR36] = 0x978, [MIB_BSCR0] = 0x9cc, [MIB_BSCR1] = 0x9d0, [MIB_BSCR2] = 0x9d4, [MIB_BSCR3] = 0x9d8, [MIB_BSCR4] = 0x9dc, [MIB_BSCR5] = 0x9e0, [MIB_BSCR6] = 0x9e4, [MIB_BSCR7] = 0x9e8, [MIB_BSCR17] = 0xa10, [MIB_TRDR1] = 0xa28, [HIF_REMAP_L1] = 0x24, [HIF_REMAP_BASE_L1] = 0x130000, [HIF_REMAP_L2] = 0x1b4, [HIF_REMAP_BASE_L2] = 0x1000, [CBTOP1_PHY_END] = 0x77ffffff, [INFRA_MCU_END] = 0x7c3fffff, [WTBLON_WDUCR] = 0x370, [WTBL_UPDATE] = 0x380, [WTBL_ITCR] = 0x3b0, [WTBL_ITCR0] = 0x3b8, [WTBL_ITCR1] = 0x3bc, }; static const u32 mt7992_offs[] = { [MIB_RVSR0] = 0x760, [MIB_RVSR1] = 0x764, [MIB_BTSCR5] = 0x7c8, [MIB_BTSCR6] = 0x7d8, [MIB_RSCR1] = 0x7f0, [MIB_RSCR27] = 0x998, [MIB_RSCR28] = 0x99c, [MIB_RSCR29] = 0x9a0, [MIB_RSCR30] = 0x9a4, [MIB_RSCR31] = 0x9a8, [MIB_RSCR33] = 0x9b0, [MIB_RSCR35] = 0x9b8, [MIB_RSCR36] = 0x9bc, [MIB_BSCR0] = 0xac8, [MIB_BSCR1] = 0xacc, [MIB_BSCR2] = 0xad0, [MIB_BSCR3] = 0xad4, [MIB_BSCR4] = 0xad8, [MIB_BSCR5] = 0xadc, [MIB_BSCR6] = 0xae0, [MIB_BSCR7] = 0xae4, [MIB_BSCR17] = 0xb0c, [MIB_TRDR1] = 0xb24, [HIF_REMAP_L1] = 0x8, [HIF_REMAP_BASE_L1] = 0x40000, [HIF_REMAP_L2] = 0x1b4, [HIF_REMAP_BASE_L2] = 0x1000, [CBTOP1_PHY_END] = 0x77ffffff, [INFRA_MCU_END] = 0x7c3fffff, [WTBLON_WDUCR] = 0x370, [WTBL_UPDATE] = 0x380, [WTBL_ITCR] = 0x3b0, [WTBL_ITCR0] = 0x3b8, [WTBL_ITCR1] = 0x3bc, }; static const u32 mt7990_offs[] = { [MIB_RVSR0] = 0x800, [MIB_RVSR1] = 0x804, [MIB_BTSCR5] = 0x868, [MIB_BTSCR6] = 0x878, [MIB_RSCR1] = 0x890, [MIB_RSCR27] = 0xa38, [MIB_RSCR28] = 0xa3c, [MIB_RSCR29] = 0xa40, [MIB_RSCR30] = 0xa44, [MIB_RSCR31] = 0xa48, [MIB_RSCR33] = 0xa50, [MIB_RSCR35] = 0xa58, [MIB_RSCR36] = 0xa5c, [MIB_BSCR0] = 0xbb8, [MIB_BSCR1] = 0xbbc, [MIB_BSCR2] = 0xbc0, [MIB_BSCR3] = 0xbc4, [MIB_BSCR4] = 0xbc8, [MIB_BSCR5] = 0xbcc, [MIB_BSCR6] = 0xbd0, [MIB_BSCR7] = 0xbd4, [MIB_BSCR17] = 0xbfc, [MIB_TRDR1] = 0xc14, [HIF_REMAP_L1] = 0x8, [HIF_REMAP_BASE_L1] = 0x40000, [HIF_REMAP_L2] = 0x1b8, [HIF_REMAP_BASE_L2] = 0x110000, [CBTOP1_PHY_END] = 0x7fffffff, [INFRA_MCU_END] = 0x7cffffff, [WTBLON_WDUCR] = 0x400, [WTBL_UPDATE] = 0x410, [WTBL_ITCR] = 0x440, [WTBL_ITCR0] = 0x448, [WTBL_ITCR1] = 0x44c, }; static const struct __map mt7996_reg_map[] = { { 0x54000000, 0x02000, 0x1000 }, /* WFDMA_0 (PCIE0 MCU DMA0) */ { 0x55000000, 0x03000, 0x1000 }, /* WFDMA_1 (PCIE0 MCU DMA1) */ { 0x56000000, 0x04000, 0x1000 }, /* WFDMA reserved */ { 0x57000000, 0x05000, 0x1000 }, /* WFDMA MCU wrap CR */ { 0x58000000, 0x06000, 0x1000 }, /* WFDMA PCIE1 MCU DMA0 (MEM_DMA) */ { 0x59000000, 0x07000, 0x1000 }, /* WFDMA PCIE1 MCU DMA1 */ { 0x820c0000, 0x08000, 0x4000 }, /* WF_UMAC_TOP (PLE) */ { 0x820c8000, 0x0c000, 0x2000 }, /* WF_UMAC_TOP (PSE) */ { 0x820cc000, 0x0e000, 0x1000 }, /* WF_UMAC_TOP (PP) */ { 0x74030000, 0x10000, 0x1000 }, /* PCIe MAC */ { 0x820e0000, 0x20000, 0x0400 }, /* WF_LMAC_TOP BN0 (WF_CFG) */ { 0x820e1000, 0x20400, 0x0200 }, /* WF_LMAC_TOP BN0 (WF_TRB) */ { 0x820e2000, 0x20800, 0x0400 }, /* WF_LMAC_TOP BN0 (WF_AGG) */ { 0x820e3000, 0x20c00, 0x0400 }, /* WF_LMAC_TOP BN0 (WF_ARB) */ { 0x820e4000, 0x21000, 0x0400 }, /* WF_LMAC_TOP BN0 (WF_TMAC) */ { 0x820e5000, 0x21400, 0x0800 }, /* WF_LMAC_TOP BN0 (WF_RMAC) */ { 0x820ce000, 0x21c00, 0x0200 }, /* WF_LMAC_TOP (WF_SEC) */ { 0x820e7000, 0x21e00, 0x0200 }, /* WF_LMAC_TOP BN0 (WF_DMA) */ { 0x820cf000, 0x22000, 0x1000 }, /* WF_LMAC_TOP (WF_PF) */ { 0x820e9000, 0x23400, 0x0200 }, /* WF_LMAC_TOP BN0 (WF_WTBLOFF) */ { 0x820ea000, 0x24000, 0x0200 }, /* WF_LMAC_TOP BN0 (WF_ETBF) */ { 0x820eb000, 0x24200, 0x0400 }, /* WF_LMAC_TOP BN0 (WF_LPON) */ { 0x820ec000, 0x24600, 0x0200 }, /* WF_LMAC_TOP BN0 (WF_INT) */ { 0x820ed000, 0x24800, 0x0800 }, /* WF_LMAC_TOP BN0 (WF_MIB) */ { 0x820ca000, 0x26000, 0x2000 }, /* WF_LMAC_TOP BN0 (WF_MUCOP) */ { 0x820d0000, 0x30000, 0x10000 }, /* WF_LMAC_TOP (WF_WTBLON) */ { 0x40000000, 0x70000, 0x10000 }, /* WF_UMAC_SYSRAM */ { 0x00400000, 0x80000, 0x10000 }, /* WF_MCU_SYSRAM */ { 0x00410000, 0x90000, 0x10000 }, /* WF_MCU_SYSRAM (configure register) */ { 0x820f0000, 0xa0000, 0x0400 }, /* WF_LMAC_TOP BN1 (WF_CFG) */ { 0x820f1000, 0xa0600, 0x0200 }, /* WF_LMAC_TOP BN1 (WF_TRB) */ { 0x820f2000, 0xa0800, 0x0400 }, /* WF_LMAC_TOP BN1 (WF_AGG) */ { 0x820f3000, 0xa0c00, 0x0400 }, /* WF_LMAC_TOP BN1 (WF_ARB) */ { 0x820f4000, 0xa1000, 0x0400 }, /* WF_LMAC_TOP BN1 (WF_TMAC) */ { 0x820f5000, 0xa1400, 0x0800 }, /* WF_LMAC_TOP BN1 (WF_RMAC) */ { 0x820f7000, 0xa1e00, 0x0200 }, /* WF_LMAC_TOP BN1 (WF_DMA) */ { 0x820f9000, 0xa3400, 0x0200 }, /* WF_LMAC_TOP BN1 (WF_WTBLOFF) */ { 0x820fa000, 0xa4000, 0x0200 }, /* WF_LMAC_TOP BN1 (WF_ETBF) */ { 0x820fb000, 0xa4200, 0x0400 }, /* WF_LMAC_TOP BN1 (WF_LPON) */ { 0x820fc000, 0xa4600, 0x0200 }, /* WF_LMAC_TOP BN1 (WF_INT) */ { 0x820fd000, 0xa4800, 0x0800 }, /* WF_LMAC_TOP BN1 (WF_MIB) */ { 0x820cc000, 0xa5000, 0x2000 }, /* WF_LMAC_TOP BN1 (WF_MUCOP) */ { 0x820c4000, 0xa8000, 0x4000 }, /* WF_LMAC_TOP BN1 (WF_MUCOP) */ { 0x820b0000, 0xae000, 0x1000 }, /* [APB2] WFSYS_ON */ { 0x80020000, 0xb0000, 0x10000 }, /* WF_TOP_MISC_OFF */ { 0x81020000, 0xc0000, 0x10000 }, /* WF_TOP_MISC_ON */ { 0x7c020000, 0xd0000, 0x10000 }, /* CONN_INFRA, wfdma */ { 0x7c060000, 0xe0000, 0x10000 }, /* CONN_INFRA, conn_host_csr_top */ { 0x7c000000, 0xf0000, 0x10000 }, /* CONN_INFRA */ { 0x0, 0x0, 0x0 }, /* imply end of search */ }; static const struct __map mt7990_reg_map[] = { {0x54000000, 0x02000, 0x1000}, /* WFDMA_0 (PCIE0 MCU DMA0) */ {0x55000000, 0x03000, 0x1000}, /* WFDMA_1 (PCIE0 MCU DMA1) */ {0x56000000, 0x04000, 0x1000}, /* WFDMA_2 (Reserved) */ {0x57000000, 0x05000, 0x1000}, /* WFDMA_3 (MCU wrap CR) */ {0x58000000, 0x06000, 0x1000}, /* WFDMA_4 (PCIE1 MCU DMA0 (MEM_DMA)) */ {0x59000000, 0x07000, 0x1000}, /* WFDMA_5 (PCIE1 MCU DMA1) */ {0x820c0000, 0x08000, 0x4000}, /* WF_UMAC_TOP (PLE) */ {0x820c8000, 0x0c000, 0x2000}, /* WF_UMAC_TOP (PSE) */ {0x820cc000, 0x0e000, 0x2000}, /* WF_UMAC_TOP (PP) */ {0x820e0000, 0x20000, 0x0400}, /* WF_LMAC_TOP BN0 (WF_CFG) */ {0x820e1000, 0x20400, 0x0200}, /* WF_LMAC_TOP BN0 (WF_TRB) */ {0x820e2000, 0x20800, 0x0400}, /* WF_LMAC_TOP BN0 (WF_AGG) */ {0x820e3000, 0x20c00, 0x0400}, /* WF_LMAC_TOP BN0 (WF_ARB) */ {0x820e4000, 0x21000, 0x0400}, /* WF_LMAC_TOP BN0 (WF_TMAC) */ {0x820e5000, 0x21400, 0x0800}, /* WF_LMAC_TOP BN0 (WF_RMAC) */ {0x820ce000, 0x21c00, 0x0200}, /* WF_LMAC_TOP (WF_SEC) */ {0x820e7000, 0x21e00, 0x0200}, /* WF_LMAC_TOP BN0 (WF_DMA) */ {0x820cf000, 0x22000, 0x1000}, /* WF_LMAC_TOP (WF_PF) */ {0x820e9000, 0x23400, 0x0200}, /* WF_LMAC_TOP BN0 (WF_WTBLOFF) */ {0x820ea000, 0x24000, 0x0200}, /* WF_LMAC_TOP BN0 (WF_ETBF) */ {0x820eb000, 0x24200, 0x0400}, /* WF_LMAC_TOP BN0 (WF_LPON) */ {0x820ec000, 0x24600, 0x0200}, /* WF_LMAC_TOP BN0 (WF_INT) */ {0x820ed000, 0x24800, 0x0800}, /* WF_LMAC_TOP BN0 (WF_MIB) */ {0x820ca000, 0x26000, 0x2000}, /* WF_LMAC_TOP BN0 (WF_MUCOP) */ {0x820d0000, 0x30000, 0x10000}, /* WF_LMAC_TOP (WF_WTBLON) */ {0x00400000, 0x80000, 0x10000}, /* WF_MCU_SYSRAM */ {0x820f0000, 0xa0000, 0x0400}, /* WF_LMAC_TOP BN1 (WF_CFG) */ {0x820f1000, 0xa0600, 0x0200}, /* WF_LMAC_TOP BN1 (WF_TRB) */ {0x820f2000, 0xa0800, 0x0400}, /* WF_LMAC_TOP BN1 (WF_AGG) */ {0x820f3000, 0xa0c00, 0x0400}, /* WF_LMAC_TOP BN1 (WF_ARB) */ {0x820f4000, 0xa1000, 0x0400}, /* WF_LMAC_TOP BN1 (WF_TMAC) */ {0x820f5000, 0xa1400, 0x0800}, /* WF_LMAC_TOP BN1 (WF_RMAC) */ {0x820f7000, 0xa1e00, 0x0200}, /* WF_LMAC_TOP BN1 (WF_DMA) */ {0x820f9000, 0xa3400, 0x0200}, /* WF_LMAC_TOP BN1 (WF_WTBLOFF) */ {0x820fa000, 0xa4000, 0x0200}, /* WF_LMAC_TOP BN1 (WF_ETBF) */ {0x820fb000, 0xa4200, 0x0400}, /* WF_LMAC_TOP BN1 (WF_LPON) */ {0x820fc000, 0xa4600, 0x0200}, /* WF_LMAC_TOP BN1 (WF_INT) */ {0x820fd000, 0xa4800, 0x0800}, /* WF_LMAC_TOP BN1 (WF_MIB) */ {0x820cc000, 0xa5000, 0x2000}, /* WF_LMAC_TOP BN1 (WF_MUCOP) */ {0x820c4000, 0xa8000, 0x4000}, /* WF_LMAC_TOP (WF_UWTBL) */ {0x81030000, 0xae000, 0x100}, /* WFSYS_AON part 1 */ {0x81031000, 0xae100, 0x100}, /* WFSYS_AON part 2 */ {0x81032000, 0xae200, 0x100}, /* WFSYS_AON part 3 */ {0x81033000, 0xae300, 0x100}, /* WFSYS_AON part 4 */ {0x81034000, 0xae400, 0x100}, /* WFSYS_AON part 5 */ {0x80020000, 0xb0000, 0x10000}, /* WF_TOP_MISC_OFF */ {0x81020000, 0xc0000, 0x10000}, /* WF_TOP_MISC_ON */ {0x81040000, 0x120000, 0x1000}, /* WF_MCU_CFG_ON */ {0x81050000, 0x121000, 0x1000}, /* WF_MCU_EINT */ {0x81060000, 0x122000, 0x1000}, /* WF_MCU_GPT */ {0x81070000, 0x123000, 0x1000}, /* WF_MCU_WDT */ {0x80010000, 0x124000, 0x1000}, /* WF_AXIDMA */ {0x7c020000, 0xd0000, 0x10000}, /* CONN_INFRA, wfdma for from CODA flow use */ {0x7c060000, 0xe0000, 0x10000}, /* CONN_INFRA, conn_host_csr_top for from CODA flow use */ {0x20020000, 0xd0000, 0x10000}, /* CONN_INFRA, wfdma */ {0x20060000, 0xe0000, 0x10000}, /* CONN_INFRA, conn_host_csr_top */ {0x7c000000, 0xf0000, 0x10000}, /* CONN_INFRA */ {0x70020000, 0x1f0000, 0x9000}, /* PCIE remapping (AP2CONN) */ {0x0, 0x0, 0x0}, /* imply end of search */ }; static u32 mt7996_reg_map_l1(struct mt7996_dev *dev, u32 addr) { u32 offset = FIELD_GET(MT_HIF_REMAP_L1_OFFSET, addr); u32 base = FIELD_GET(MT_HIF_REMAP_L1_BASE, addr); u32 l1_mask, val; if (is_mt7996(&dev->mt76)) { l1_mask = MT_HIF_REMAP_L1_MASK_7996; val = FIELD_PREP(MT_HIF_REMAP_L1_MASK_7996, base); } else { l1_mask = MT_HIF_REMAP_L1_MASK; val = FIELD_PREP(MT_HIF_REMAP_L1_MASK, base); } dev->bus_ops->rmw(&dev->mt76, MT_HIF_REMAP_L1, l1_mask, val); /* use read to push write */ dev->bus_ops->rr(&dev->mt76, MT_HIF_REMAP_L1); return MT_HIF_REMAP_BASE_L1 + offset; } static u32 mt7996_reg_map_l2(struct mt7996_dev *dev, u32 addr) { u32 offset, base, l2_mask, val; if (is_mt7990(&dev->mt76)) { offset = FIELD_GET(MT_HIF_REMAP_L2_OFFSET_7990, addr); base = FIELD_GET(MT_HIF_REMAP_L2_BASE_7990, addr); l2_mask = MT_HIF_REMAP_L2_MASK_7990; val = FIELD_PREP(MT_HIF_REMAP_L2_MASK_7990, base); } else { offset = FIELD_GET(MT_HIF_REMAP_L2_OFFSET, addr); base = FIELD_GET(MT_HIF_REMAP_L2_BASE, addr); l2_mask = MT_HIF_REMAP_L2_MASK; val = FIELD_PREP(MT_HIF_REMAP_L2_MASK, base); } dev->bus_ops->rmw(&dev->mt76, MT_HIF_REMAP_L2, l2_mask, val); /* use read to push write */ dev->bus_ops->rr(&dev->mt76, MT_HIF_REMAP_L2); return MT_HIF_REMAP_BASE_L2 + offset; } static u32 mt7996_reg_map_cbtop(struct mt7996_dev *dev, u32 addr) { u32 offset = FIELD_GET(MT_HIF_REMAP_CBTOP_OFFSET, addr); u32 base = FIELD_GET(MT_HIF_REMAP_CBTOP_BASE, addr); dev->bus_ops->rmw(&dev->mt76, MT_HIF_REMAP_CBTOP, MT_HIF_REMAP_CBTOP_MASK, FIELD_PREP(MT_HIF_REMAP_CBTOP_MASK, base)); /* use read to push write */ dev->bus_ops->rr(&dev->mt76, MT_HIF_REMAP_CBTOP); return MT_HIF_REMAP_BASE_CBTOP + offset; } static u32 __mt7996_reg_addr(struct mt7996_dev *dev, u32 addr) { int i; if (addr < 0x100000) return addr; for (i = 0; i < dev->reg.map_size; i++) { u32 ofs; if (addr < dev->reg.map[i].phys) continue; ofs = addr - dev->reg.map[i].phys; if (ofs >= dev->reg.map[i].size) continue; return dev->reg.map[i].mapped + ofs; } return 0; } static u32 __mt7996_reg_remap_addr(struct mt7996_dev *dev, u32 addr) { if ((addr >= MT_INFRA_BASE && addr < MT_WFSYS0_PHY_START) || (addr >= MT_WFSYS0_PHY_START && addr < MT_WFSYS1_PHY_START) || (addr >= MT_WFSYS1_PHY_START && addr <= MT_WFSYS1_PHY_END)) return mt7996_reg_map_l1(dev, addr); /* CONN_INFRA: covert to phyiscal addr and use layer 1 remap */ if (addr >= MT_INFRA_MCU_START && addr <= MT_INFRA_MCU_END) { addr = addr - MT_INFRA_MCU_START + MT_INFRA_BASE; return mt7996_reg_map_l1(dev, addr); } if (dev_is_pci(dev->mt76.dev) && ((addr >= MT_CBTOP1_PHY_START && addr <= MT_CBTOP1_PHY_END) || addr >= MT_CBTOP2_PHY_START)) { if (is_mt7990(&dev->mt76)) return mt7996_reg_map_cbtop(dev, addr); return mt7996_reg_map_l1(dev, addr); } return mt7996_reg_map_l2(dev, addr); } void mt7996_memcpy_fromio(struct mt7996_dev *dev, void *buf, u32 offset, size_t len) { u32 addr = __mt7996_reg_addr(dev, offset); if (addr) { #if defined(__linux__) memcpy_fromio(buf, dev->mt76.mmio.regs + addr, len); #elif defined(__FreeBSD__) memcpy_fromio(buf, (u8 *)dev->mt76.mmio.regs + addr, len); #endif return; } spin_lock_bh(&dev->reg_lock); #if defined(__linux__) memcpy_fromio(buf, dev->mt76.mmio.regs + #elif defined(__FreeBSD__) memcpy_fromio(buf, (u8 *)dev->mt76.mmio.regs + #endif __mt7996_reg_remap_addr(dev, offset), len); spin_unlock_bh(&dev->reg_lock); } static u32 mt7996_rr(struct mt76_dev *mdev, u32 offset) { struct mt7996_dev *dev = container_of(mdev, struct mt7996_dev, mt76); u32 addr = __mt7996_reg_addr(dev, offset), val; if (addr) return dev->bus_ops->rr(mdev, addr); spin_lock_bh(&dev->reg_lock); val = dev->bus_ops->rr(mdev, __mt7996_reg_remap_addr(dev, offset)); spin_unlock_bh(&dev->reg_lock); return val; } static void mt7996_wr(struct mt76_dev *mdev, u32 offset, u32 val) { struct mt7996_dev *dev = container_of(mdev, struct mt7996_dev, mt76); u32 addr = __mt7996_reg_addr(dev, offset); if (addr) { dev->bus_ops->wr(mdev, addr, val); return; } spin_lock_bh(&dev->reg_lock); dev->bus_ops->wr(mdev, __mt7996_reg_remap_addr(dev, offset), val); spin_unlock_bh(&dev->reg_lock); } static u32 mt7996_rmw(struct mt76_dev *mdev, u32 offset, u32 mask, u32 val) { struct mt7996_dev *dev = container_of(mdev, struct mt7996_dev, mt76); u32 addr = __mt7996_reg_addr(dev, offset); if (addr) return dev->bus_ops->rmw(mdev, addr, mask, val); spin_lock_bh(&dev->reg_lock); val = dev->bus_ops->rmw(mdev, __mt7996_reg_remap_addr(dev, offset), mask, val); spin_unlock_bh(&dev->reg_lock); return val; } #ifdef CONFIG_NET_MEDIATEK_SOC_WED static int mt7996_mmio_wed_reset(struct mtk_wed_device *wed) { struct mt76_dev *mdev = container_of(wed, struct mt76_dev, mmio.wed); struct mt7996_dev *dev = container_of(mdev, struct mt7996_dev, mt76); struct mt76_phy *mphy = &dev->mphy; int ret; ASSERT_RTNL(); if (test_and_set_bit(MT76_STATE_WED_RESET, &mphy->state)) return -EBUSY; ret = mt7996_mcu_set_ser(dev, UNI_CMD_SER_TRIGGER, UNI_CMD_SER_SET_RECOVER_FROM_ETH, mphy->band_idx); if (ret) goto out; rtnl_unlock(); if (!wait_for_completion_timeout(&mdev->mmio.wed_reset, 20 * HZ)) { dev_err(mdev->dev, "wed reset timeout\n"); ret = -ETIMEDOUT; } rtnl_lock(); out: clear_bit(MT76_STATE_WED_RESET, &mphy->state); return ret; } #endif int mt7996_mmio_wed_init(struct mt7996_dev *dev, void *pdev_ptr, bool hif2, int *irq) { #ifdef CONFIG_NET_MEDIATEK_SOC_WED struct mtk_wed_device *wed = &dev->mt76.mmio.wed; struct pci_dev *pci_dev = pdev_ptr; u32 hif1_ofs = 0; if (!wed_enable) return 0; dev->has_rro = true; hif1_ofs = MT_WFDMA0_PCIE1(0) - MT_WFDMA0(0); if (hif2) wed = &dev->mt76.mmio.wed_hif2; wed->wlan.pci_dev = pci_dev; wed->wlan.bus_type = MTK_WED_BUS_PCIE; wed->wlan.base = devm_ioremap(dev->mt76.dev, pci_resource_start(pci_dev, 0), pci_resource_len(pci_dev, 0)); if (!wed->wlan.base) return -ENOMEM; wed->wlan.phy_base = pci_resource_start(pci_dev, 0); if (hif2) { wed->wlan.wpdma_int = wed->wlan.phy_base + MT_INT_PCIE1_SOURCE_CSR_EXT; wed->wlan.wpdma_mask = wed->wlan.phy_base + MT_INT_PCIE1_MASK_CSR; wed->wlan.wpdma_tx = wed->wlan.phy_base + hif1_ofs + MT_TXQ_RING_BASE(0) + MT7996_TXQ_BAND2 * MT_RING_SIZE; if (dev->has_rro) { wed->wlan.wpdma_txfree = wed->wlan.phy_base + hif1_ofs + MT_RXQ_RING_BASE(0) + MT7996_RXQ_TXFREE2 * MT_RING_SIZE; wed->wlan.txfree_tbit = ffs(MT_INT_RX_TXFREE_EXT) - 1; } else { wed->wlan.wpdma_txfree = wed->wlan.phy_base + hif1_ofs + MT_RXQ_RING_BASE(0) + MT7996_RXQ_MCU_WA_TRI * MT_RING_SIZE; wed->wlan.txfree_tbit = ffs(MT_INT_RX_DONE_WA_TRI) - 1; } wed->wlan.wpdma_rx_glo = wed->wlan.phy_base + hif1_ofs + MT_WFDMA0_GLO_CFG; wed->wlan.wpdma_rx = wed->wlan.phy_base + hif1_ofs + MT_RXQ_RING_BASE(MT7996_RXQ_BAND0) + MT7996_RXQ_BAND0 * MT_RING_SIZE; wed->wlan.id = MT7996_DEVICE_ID_2; wed->wlan.tx_tbit[0] = ffs(MT_INT_TX_DONE_BAND2) - 1; } else { wed->wlan.hw_rro = dev->has_rro; /* default on */ wed->wlan.wpdma_int = wed->wlan.phy_base + MT_INT_SOURCE_CSR; wed->wlan.wpdma_mask = wed->wlan.phy_base + MT_INT_MASK_CSR; wed->wlan.wpdma_tx = wed->wlan.phy_base + MT_TXQ_RING_BASE(0) + MT7996_TXQ_BAND0 * MT_RING_SIZE; wed->wlan.wpdma_rx_glo = wed->wlan.phy_base + MT_WFDMA0_GLO_CFG; wed->wlan.wpdma_rx = wed->wlan.phy_base + MT_RXQ_RING_BASE(MT7996_RXQ_BAND0) + MT7996_RXQ_BAND0 * MT_RING_SIZE; wed->wlan.wpdma_rx_rro[0] = wed->wlan.phy_base + MT_RXQ_RING_BASE(MT7996_RXQ_RRO_BAND0) + MT7996_RXQ_RRO_BAND0 * MT_RING_SIZE; wed->wlan.wpdma_rx_rro[1] = wed->wlan.phy_base + hif1_ofs + MT_RXQ_RING_BASE(MT7996_RXQ_RRO_BAND2) + MT7996_RXQ_RRO_BAND2 * MT_RING_SIZE; wed->wlan.wpdma_rx_pg = wed->wlan.phy_base + MT_RXQ_RING_BASE(MT7996_RXQ_MSDU_PG_BAND0) + MT7996_RXQ_MSDU_PG_BAND0 * MT_RING_SIZE; wed->wlan.rx_nbuf = 65536; wed->wlan.rx_npkt = dev->hif2 ? 32768 : 24576; wed->wlan.rx_size = SKB_WITH_OVERHEAD(MT_RX_BUF_SIZE); wed->wlan.rx_tbit[0] = ffs(MT_INT_RX_DONE_BAND0) - 1; wed->wlan.rx_tbit[1] = ffs(MT_INT_RX_DONE_BAND2) - 1; wed->wlan.rro_rx_tbit[0] = ffs(MT_INT_RX_DONE_RRO_BAND0) - 1; wed->wlan.rro_rx_tbit[1] = ffs(MT_INT_RX_DONE_RRO_BAND2) - 1; wed->wlan.rx_pg_tbit[0] = ffs(MT_INT_RX_DONE_MSDU_PG_BAND0) - 1; wed->wlan.rx_pg_tbit[1] = ffs(MT_INT_RX_DONE_MSDU_PG_BAND1) - 1; wed->wlan.rx_pg_tbit[2] = ffs(MT_INT_RX_DONE_MSDU_PG_BAND2) - 1; wed->wlan.tx_tbit[0] = ffs(MT_INT_TX_DONE_BAND0) - 1; wed->wlan.tx_tbit[1] = ffs(MT_INT_TX_DONE_BAND1) - 1; if (dev->has_rro) { wed->wlan.wpdma_txfree = wed->wlan.phy_base + MT_RXQ_RING_BASE(0) + MT7996_RXQ_TXFREE0 * MT_RING_SIZE; wed->wlan.txfree_tbit = ffs(MT_INT_RX_TXFREE_MAIN) - 1; } else { wed->wlan.txfree_tbit = ffs(MT_INT_RX_DONE_WA_MAIN) - 1; wed->wlan.wpdma_txfree = wed->wlan.phy_base + MT_RXQ_RING_BASE(0) + MT7996_RXQ_MCU_WA_MAIN * MT_RING_SIZE; } dev->mt76.rx_token_size = MT7996_TOKEN_SIZE + wed->wlan.rx_npkt; } wed->wlan.nbuf = MT7996_HW_TOKEN_SIZE; wed->wlan.token_start = MT7996_TOKEN_SIZE - wed->wlan.nbuf; wed->wlan.amsdu_max_subframes = 8; wed->wlan.amsdu_max_len = 1536; wed->wlan.init_buf = mt7996_wed_init_buf; wed->wlan.init_rx_buf = mt76_wed_init_rx_buf; wed->wlan.release_rx_buf = mt76_wed_release_rx_buf; wed->wlan.offload_enable = mt76_wed_offload_enable; wed->wlan.offload_disable = mt76_wed_offload_disable; if (!hif2) { wed->wlan.reset = mt7996_mmio_wed_reset; wed->wlan.reset_complete = mt76_wed_reset_complete; } if (mtk_wed_device_attach(wed)) return 0; *irq = wed->irq; dev->mt76.dma_dev = wed->dev; return 1; #else return 0; #endif } static int mt7996_mmio_init(struct mt76_dev *mdev, void __iomem *mem_base, u32 device_id) { struct mt76_bus_ops *bus_ops; struct mt7996_dev *dev; dev = container_of(mdev, struct mt7996_dev, mt76); mt76_mmio_init(&dev->mt76, mem_base); spin_lock_init(&dev->reg_lock); switch (device_id) { case MT7996_DEVICE_ID: dev->reg.base = mt7996_reg_base; dev->reg.offs_rev = mt7996_offs; dev->reg.map = mt7996_reg_map; dev->reg.map_size = ARRAY_SIZE(mt7996_reg_map); break; case MT7992_DEVICE_ID: dev->reg.base = mt7996_reg_base; dev->reg.offs_rev = mt7992_offs; dev->reg.map = mt7996_reg_map; dev->reg.map_size = ARRAY_SIZE(mt7996_reg_map); break; case MT7990_DEVICE_ID: dev->reg.base = mt7996_reg_base; dev->reg.offs_rev = mt7990_offs; dev->reg.map = mt7990_reg_map; dev->reg.map_size = ARRAY_SIZE(mt7990_reg_map); break; default: return -EINVAL; } dev->bus_ops = dev->mt76.bus; bus_ops = devm_kmemdup(dev->mt76.dev, dev->bus_ops, sizeof(*bus_ops), GFP_KERNEL); if (!bus_ops) return -ENOMEM; bus_ops->rr = mt7996_rr; bus_ops->wr = mt7996_wr; bus_ops->rmw = mt7996_rmw; dev->mt76.bus = bus_ops; mdev->rev = (device_id << 16) | (mt76_rr(dev, MT_HW_REV) & 0xff); dev_dbg(mdev->dev, "ASIC revision: %04x\n", mdev->rev); return 0; } void mt7996_dual_hif_set_irq_mask(struct mt7996_dev *dev, bool write_reg, u32 clear, u32 set) { struct mt76_dev *mdev = &dev->mt76; unsigned long flags; spin_lock_irqsave(&mdev->mmio.irq_lock, flags); mdev->mmio.irqmask &= ~clear; mdev->mmio.irqmask |= set; if (write_reg) { if (mtk_wed_device_active(&mdev->mmio.wed)) { mtk_wed_device_irq_set_mask(&mdev->mmio.wed, mdev->mmio.irqmask); if (mtk_wed_device_active(&mdev->mmio.wed_hif2)) { mtk_wed_device_irq_set_mask(&mdev->mmio.wed_hif2, mdev->mmio.irqmask); } } else { mt76_wr(dev, MT_INT_MASK_CSR, mdev->mmio.irqmask); mt76_wr(dev, MT_INT1_MASK_CSR, mdev->mmio.irqmask); } } spin_unlock_irqrestore(&mdev->mmio.irq_lock, flags); } static void mt7996_rx_poll_complete(struct mt76_dev *mdev, enum mt76_rxq_id q) { struct mt7996_dev *dev = container_of(mdev, struct mt7996_dev, mt76); mt7996_irq_enable(dev, MT_INT_RX(q)); } /* TODO: support 2/4/6/8 MSI-X vectors */ static void mt7996_irq_tasklet(struct tasklet_struct *t) { struct mt7996_dev *dev = from_tasklet(dev, t, mt76.irq_tasklet); struct mtk_wed_device *wed = &dev->mt76.mmio.wed; struct mtk_wed_device *wed_hif2 = &dev->mt76.mmio.wed_hif2; u32 i, intr, mask, intr1 = 0; if (dev->hif2 && mtk_wed_device_active(wed_hif2)) { mtk_wed_device_irq_set_mask(wed_hif2, 0); intr1 = mtk_wed_device_irq_get(wed_hif2, dev->mt76.mmio.irqmask); if (intr1 & MT_INT_RX_TXFREE_EXT) napi_schedule(&dev->mt76.napi[MT_RXQ_TXFREE_BAND2]); } if (mtk_wed_device_active(wed)) { mtk_wed_device_irq_set_mask(wed, 0); intr = mtk_wed_device_irq_get(wed, dev->mt76.mmio.irqmask); intr |= (intr1 & ~MT_INT_RX_TXFREE_EXT); } else { mt76_wr(dev, MT_INT_MASK_CSR, 0); if (dev->hif2) mt76_wr(dev, MT_INT1_MASK_CSR, 0); intr = mt76_rr(dev, MT_INT_SOURCE_CSR); intr &= dev->mt76.mmio.irqmask; mt76_wr(dev, MT_INT_SOURCE_CSR, intr); if (dev->hif2) { intr1 = mt76_rr(dev, MT_INT1_SOURCE_CSR); intr1 &= dev->mt76.mmio.irqmask; mt76_wr(dev, MT_INT1_SOURCE_CSR, intr1); intr |= intr1; } } trace_dev_irq(&dev->mt76, intr, dev->mt76.mmio.irqmask); mask = intr & MT_INT_RX_DONE_ALL; if (intr & MT_INT_TX_DONE_MCU) mask |= MT_INT_TX_DONE_MCU; mt7996_irq_disable(dev, mask); if (intr & MT_INT_TX_DONE_MCU) napi_schedule(&dev->mt76.tx_napi); for (i = 0; i < __MT_RXQ_MAX; i++) { if ((intr & MT_INT_RX(i))) napi_schedule(&dev->mt76.napi[i]); } if (intr & MT_INT_MCU_CMD) { u32 val = mt76_rr(dev, MT_MCU_CMD); mt76_wr(dev, MT_MCU_CMD, val); if (val & (MT_MCU_CMD_ERROR_MASK | MT_MCU_CMD_WDT_MASK)) { dev->recovery.state = val; mt7996_reset(dev); } } } irqreturn_t mt7996_irq_handler(int irq, void *dev_instance) { struct mt7996_dev *dev = dev_instance; if (mtk_wed_device_active(&dev->mt76.mmio.wed)) mtk_wed_device_irq_set_mask(&dev->mt76.mmio.wed, 0); else mt76_wr(dev, MT_INT_MASK_CSR, 0); if (dev->hif2) { if (mtk_wed_device_active(&dev->mt76.mmio.wed_hif2)) mtk_wed_device_irq_set_mask(&dev->mt76.mmio.wed_hif2, 0); else mt76_wr(dev, MT_INT1_MASK_CSR, 0); } if (!test_bit(MT76_STATE_INITIALIZED, &dev->mphy.state)) return IRQ_NONE; tasklet_schedule(&dev->mt76.irq_tasklet); return IRQ_HANDLED; } struct mt7996_dev *mt7996_mmio_probe(struct device *pdev, void __iomem *mem_base, u32 device_id) { static const struct mt76_driver_ops drv_ops = { /* txwi_size = txd size + txp size */ .txwi_size = MT_TXD_SIZE + sizeof(struct mt76_connac_fw_txp), .link_data_size = sizeof(struct mt7996_vif_link), .drv_flags = MT_DRV_TXWI_NO_FREE | MT_DRV_AMSDU_OFFLOAD | MT_DRV_HW_MGMT_TXQ, .survey_flags = SURVEY_INFO_TIME_TX | SURVEY_INFO_TIME_RX | SURVEY_INFO_TIME_BSS_RX, .token_size = MT7996_TOKEN_SIZE, .tx_prepare_skb = mt7996_tx_prepare_skb, .tx_complete_skb = mt76_connac_tx_complete_skb, .rx_skb = mt7996_queue_rx_skb, .rx_check = mt7996_rx_check, .rx_poll_complete = mt7996_rx_poll_complete, .update_survey = mt7996_update_channel, .set_channel = mt7996_set_channel, .vif_link_add = mt7996_vif_link_add, .vif_link_remove = mt7996_vif_link_remove, }; struct mt7996_dev *dev; struct mt76_dev *mdev; int ret; mdev = mt76_alloc_device(pdev, sizeof(*dev), &mt7996_ops, &drv_ops); if (!mdev) return ERR_PTR(-ENOMEM); dev = container_of(mdev, struct mt7996_dev, mt76); ret = mt7996_mmio_init(mdev, mem_base, device_id); if (ret) goto error; tasklet_setup(&mdev->irq_tasklet, mt7996_irq_tasklet); mt76_wr(dev, MT_INT_MASK_CSR, 0); return dev; error: mt76_free_device(&dev->mt76); return ERR_PTR(ret); } static int __init mt7996_init(void) { int ret; ret = pci_register_driver(&mt7996_hif_driver); if (ret) return ret; ret = pci_register_driver(&mt7996_pci_driver); if (ret) pci_unregister_driver(&mt7996_hif_driver); return ret; } static void __exit mt7996_exit(void) { pci_unregister_driver(&mt7996_pci_driver); pci_unregister_driver(&mt7996_hif_driver); } module_init(mt7996_init); module_exit(mt7996_exit); MODULE_DESCRIPTION("MediaTek MT7996 MMIO helpers"); MODULE_LICENSE("Dual BSD/GPL"); diff --git a/sys/contrib/dev/mediatek/mt76/usb.c b/sys/contrib/dev/mediatek/mt76/usb.c index f9e67b8c3b3c..11f9d2808f15 100644 --- a/sys/contrib/dev/mediatek/mt76/usb.c +++ b/sys/contrib/dev/mediatek/mt76/usb.c @@ -1,1139 +1,1143 @@ // SPDX-License-Identifier: ISC /* * Copyright (C) 2018 Lorenzo Bianconi */ +#if defined(__FreeBSD__) +#define LINUXKPI_PARAM_PREFIX mt76_usb_ +#endif + #include #include "mt76.h" #include "usb_trace.h" #include "dma.h" #define MT_VEND_REQ_MAX_RETRY 10 #define MT_VEND_REQ_TOUT_MS 300 static bool disable_usb_sg; module_param_named(disable_usb_sg, disable_usb_sg, bool, 0644); MODULE_PARM_DESC(disable_usb_sg, "Disable usb scatter-gather support"); int __mt76u_vendor_request(struct mt76_dev *dev, u8 req, u8 req_type, u16 val, u16 offset, void *buf, size_t len) { struct usb_interface *uintf = to_usb_interface(dev->dev); struct usb_device *udev = interface_to_usbdev(uintf); unsigned int pipe; int i, ret; lockdep_assert_held(&dev->usb.usb_ctrl_mtx); pipe = (req_type & USB_DIR_IN) ? usb_rcvctrlpipe(udev, 0) : usb_sndctrlpipe(udev, 0); for (i = 0; i < MT_VEND_REQ_MAX_RETRY; i++) { if (test_bit(MT76_REMOVED, &dev->phy.state)) return -EIO; ret = usb_control_msg(udev, pipe, req, req_type, val, offset, buf, len, MT_VEND_REQ_TOUT_MS); if (ret == -ENODEV || ret == -EPROTO) set_bit(MT76_REMOVED, &dev->phy.state); if (ret >= 0 || ret == -ENODEV || ret == -EPROTO) return ret; usleep_range(5000, 10000); } dev_err(dev->dev, "vendor request req:%02x off:%04x failed:%d\n", req, offset, ret); return ret; } EXPORT_SYMBOL_GPL(__mt76u_vendor_request); int mt76u_vendor_request(struct mt76_dev *dev, u8 req, u8 req_type, u16 val, u16 offset, void *buf, size_t len) { int ret; mutex_lock(&dev->usb.usb_ctrl_mtx); ret = __mt76u_vendor_request(dev, req, req_type, val, offset, buf, len); trace_usb_reg_wr(dev, offset, val); mutex_unlock(&dev->usb.usb_ctrl_mtx); return ret; } EXPORT_SYMBOL_GPL(mt76u_vendor_request); u32 ___mt76u_rr(struct mt76_dev *dev, u8 req, u8 req_type, u32 addr) { struct mt76_usb *usb = &dev->usb; u32 data = ~0; int ret; ret = __mt76u_vendor_request(dev, req, req_type, addr >> 16, addr, usb->data, sizeof(__le32)); if (ret == sizeof(__le32)) data = get_unaligned_le32(usb->data); trace_usb_reg_rr(dev, addr, data); return data; } EXPORT_SYMBOL_GPL(___mt76u_rr); static u32 __mt76u_rr(struct mt76_dev *dev, u32 addr) { u8 req; switch (addr & MT_VEND_TYPE_MASK) { case MT_VEND_TYPE_EEPROM: req = MT_VEND_READ_EEPROM; break; case MT_VEND_TYPE_CFG: req = MT_VEND_READ_CFG; break; default: req = MT_VEND_MULTI_READ; break; } return ___mt76u_rr(dev, req, USB_DIR_IN | USB_TYPE_VENDOR, addr & ~MT_VEND_TYPE_MASK); } static u32 mt76u_rr(struct mt76_dev *dev, u32 addr) { u32 ret; mutex_lock(&dev->usb.usb_ctrl_mtx); ret = __mt76u_rr(dev, addr); mutex_unlock(&dev->usb.usb_ctrl_mtx); return ret; } void ___mt76u_wr(struct mt76_dev *dev, u8 req, u8 req_type, u32 addr, u32 val) { struct mt76_usb *usb = &dev->usb; put_unaligned_le32(val, usb->data); __mt76u_vendor_request(dev, req, req_type, addr >> 16, addr, usb->data, sizeof(__le32)); trace_usb_reg_wr(dev, addr, val); } EXPORT_SYMBOL_GPL(___mt76u_wr); static void __mt76u_wr(struct mt76_dev *dev, u32 addr, u32 val) { u8 req; switch (addr & MT_VEND_TYPE_MASK) { case MT_VEND_TYPE_CFG: req = MT_VEND_WRITE_CFG; break; default: req = MT_VEND_MULTI_WRITE; break; } ___mt76u_wr(dev, req, USB_DIR_OUT | USB_TYPE_VENDOR, addr & ~MT_VEND_TYPE_MASK, val); } static void mt76u_wr(struct mt76_dev *dev, u32 addr, u32 val) { mutex_lock(&dev->usb.usb_ctrl_mtx); __mt76u_wr(dev, addr, val); mutex_unlock(&dev->usb.usb_ctrl_mtx); } static u32 mt76u_rmw(struct mt76_dev *dev, u32 addr, u32 mask, u32 val) { mutex_lock(&dev->usb.usb_ctrl_mtx); val |= __mt76u_rr(dev, addr) & ~mask; __mt76u_wr(dev, addr, val); mutex_unlock(&dev->usb.usb_ctrl_mtx); return val; } static void mt76u_copy(struct mt76_dev *dev, u32 offset, const void *data, int len) { struct mt76_usb *usb = &dev->usb; const u8 *val = data; int ret; int current_batch_size; int i = 0; /* Assure that always a multiple of 4 bytes are copied, * otherwise beacons can be corrupted. * See: "mt76: round up length on mt76_wr_copy" * Commit 850e8f6fbd5d0003b0 */ len = round_up(len, 4); mutex_lock(&usb->usb_ctrl_mtx); while (i < len) { current_batch_size = min_t(int, usb->data_len, len - i); memcpy(usb->data, val + i, current_batch_size); ret = __mt76u_vendor_request(dev, MT_VEND_MULTI_WRITE, USB_DIR_OUT | USB_TYPE_VENDOR, 0, offset + i, usb->data, current_batch_size); if (ret < 0) break; i += current_batch_size; } mutex_unlock(&usb->usb_ctrl_mtx); } void mt76u_read_copy(struct mt76_dev *dev, u32 offset, void *data, int len) { struct mt76_usb *usb = &dev->usb; int i = 0, batch_len, ret; u8 *val = data; len = round_up(len, 4); mutex_lock(&usb->usb_ctrl_mtx); while (i < len) { batch_len = min_t(int, usb->data_len, len - i); ret = __mt76u_vendor_request(dev, MT_VEND_READ_EXT, USB_DIR_IN | USB_TYPE_VENDOR, (offset + i) >> 16, offset + i, usb->data, batch_len); if (ret < 0) break; memcpy(val + i, usb->data, batch_len); i += batch_len; } mutex_unlock(&usb->usb_ctrl_mtx); } EXPORT_SYMBOL_GPL(mt76u_read_copy); void mt76u_single_wr(struct mt76_dev *dev, const u8 req, const u16 offset, const u32 val) { mutex_lock(&dev->usb.usb_ctrl_mtx); __mt76u_vendor_request(dev, req, USB_DIR_OUT | USB_TYPE_VENDOR, val & 0xffff, offset, NULL, 0); __mt76u_vendor_request(dev, req, USB_DIR_OUT | USB_TYPE_VENDOR, val >> 16, offset + 2, NULL, 0); mutex_unlock(&dev->usb.usb_ctrl_mtx); } EXPORT_SYMBOL_GPL(mt76u_single_wr); static int mt76u_req_wr_rp(struct mt76_dev *dev, u32 base, const struct mt76_reg_pair *data, int len) { struct mt76_usb *usb = &dev->usb; mutex_lock(&usb->usb_ctrl_mtx); while (len > 0) { __mt76u_wr(dev, base + data->reg, data->value); len--; data++; } mutex_unlock(&usb->usb_ctrl_mtx); return 0; } static int mt76u_wr_rp(struct mt76_dev *dev, u32 base, const struct mt76_reg_pair *data, int n) { if (test_bit(MT76_STATE_MCU_RUNNING, &dev->phy.state)) return dev->mcu_ops->mcu_wr_rp(dev, base, data, n); else return mt76u_req_wr_rp(dev, base, data, n); } static int mt76u_req_rd_rp(struct mt76_dev *dev, u32 base, struct mt76_reg_pair *data, int len) { struct mt76_usb *usb = &dev->usb; mutex_lock(&usb->usb_ctrl_mtx); while (len > 0) { data->value = __mt76u_rr(dev, base + data->reg); len--; data++; } mutex_unlock(&usb->usb_ctrl_mtx); return 0; } static int mt76u_rd_rp(struct mt76_dev *dev, u32 base, struct mt76_reg_pair *data, int n) { if (test_bit(MT76_STATE_MCU_RUNNING, &dev->phy.state)) return dev->mcu_ops->mcu_rd_rp(dev, base, data, n); else return mt76u_req_rd_rp(dev, base, data, n); } static bool mt76u_check_sg(struct mt76_dev *dev) { struct usb_interface *uintf = to_usb_interface(dev->dev); struct usb_device *udev = interface_to_usbdev(uintf); return (!disable_usb_sg && udev->bus->sg_tablesize > 0 && udev->bus->no_sg_constraint); } static int mt76u_set_endpoints(struct usb_interface *intf, struct mt76_usb *usb) { struct usb_host_interface *intf_desc = intf->cur_altsetting; struct usb_endpoint_descriptor *ep_desc; int i, in_ep = 0, out_ep = 0; for (i = 0; i < intf_desc->desc.bNumEndpoints; i++) { ep_desc = &intf_desc->endpoint[i].desc; if (usb_endpoint_is_bulk_in(ep_desc) && in_ep < __MT_EP_IN_MAX) { usb->in_ep[in_ep] = usb_endpoint_num(ep_desc); in_ep++; } else if (usb_endpoint_is_bulk_out(ep_desc) && out_ep < __MT_EP_OUT_MAX) { usb->out_ep[out_ep] = usb_endpoint_num(ep_desc); out_ep++; } } if (in_ep != __MT_EP_IN_MAX || out_ep != __MT_EP_OUT_MAX) return -EINVAL; return 0; } static int mt76u_fill_rx_sg(struct mt76_dev *dev, struct mt76_queue *q, struct urb *urb, int nsgs) { int i; for (i = 0; i < nsgs; i++) { void *data; int offset; data = mt76_get_page_pool_buf(q, &offset, q->buf_size); if (!data) break; sg_set_page(&urb->sg[i], virt_to_head_page(data), q->buf_size, offset); } if (i < nsgs) { int j; for (j = nsgs; j < urb->num_sgs; j++) mt76_put_page_pool_buf(sg_virt(&urb->sg[j]), false); urb->num_sgs = i; } urb->num_sgs = max_t(int, i, urb->num_sgs); urb->transfer_buffer_length = urb->num_sgs * q->buf_size; sg_init_marker(urb->sg, urb->num_sgs); return i ? : -ENOMEM; } static int mt76u_refill_rx(struct mt76_dev *dev, struct mt76_queue *q, struct urb *urb, int nsgs) { enum mt76_rxq_id qid = q - &dev->q_rx[MT_RXQ_MAIN]; int offset; if (qid == MT_RXQ_MAIN && dev->usb.sg_en) return mt76u_fill_rx_sg(dev, q, urb, nsgs); urb->transfer_buffer_length = q->buf_size; urb->transfer_buffer = mt76_get_page_pool_buf(q, &offset, q->buf_size); return urb->transfer_buffer ? 0 : -ENOMEM; } static int mt76u_urb_alloc(struct mt76_dev *dev, struct mt76_queue_entry *e, int sg_max_size) { unsigned int size = sizeof(struct urb); if (dev->usb.sg_en) size += sg_max_size * sizeof(struct scatterlist); e->urb = kzalloc(size, GFP_KERNEL); if (!e->urb) return -ENOMEM; usb_init_urb(e->urb); if (dev->usb.sg_en && sg_max_size > 0) e->urb->sg = (struct scatterlist *)(e->urb + 1); return 0; } static int mt76u_rx_urb_alloc(struct mt76_dev *dev, struct mt76_queue *q, struct mt76_queue_entry *e) { enum mt76_rxq_id qid = q - &dev->q_rx[MT_RXQ_MAIN]; int err, sg_size; sg_size = qid == MT_RXQ_MAIN ? MT_RX_SG_MAX_SIZE : 0; err = mt76u_urb_alloc(dev, e, sg_size); if (err) return err; return mt76u_refill_rx(dev, q, e->urb, sg_size); } static void mt76u_urb_free(struct urb *urb) { int i; for (i = 0; i < urb->num_sgs; i++) mt76_put_page_pool_buf(sg_virt(&urb->sg[i]), false); if (urb->transfer_buffer) mt76_put_page_pool_buf(urb->transfer_buffer, false); usb_free_urb(urb); } static void mt76u_fill_bulk_urb(struct mt76_dev *dev, int dir, int index, struct urb *urb, usb_complete_t complete_fn, void *context) { struct usb_interface *uintf = to_usb_interface(dev->dev); struct usb_device *udev = interface_to_usbdev(uintf); unsigned int pipe; if (dir == USB_DIR_IN) pipe = usb_rcvbulkpipe(udev, dev->usb.in_ep[index]); else pipe = usb_sndbulkpipe(udev, dev->usb.out_ep[index]); urb->dev = udev; urb->pipe = pipe; urb->complete = complete_fn; urb->context = context; } static struct urb * mt76u_get_next_rx_entry(struct mt76_queue *q) { struct urb *urb = NULL; unsigned long flags; spin_lock_irqsave(&q->lock, flags); if (q->queued > 0) { urb = q->entry[q->tail].urb; q->tail = (q->tail + 1) % q->ndesc; q->queued--; } spin_unlock_irqrestore(&q->lock, flags); return urb; } static int mt76u_get_rx_entry_len(struct mt76_dev *dev, u8 *data, u32 data_len) { u16 dma_len, min_len; dma_len = get_unaligned_le16(data); if (dev->drv->drv_flags & MT_DRV_RX_DMA_HDR) return dma_len; min_len = MT_DMA_HDR_LEN + MT_RX_RXWI_LEN + MT_FCE_INFO_LEN; if (data_len < min_len || !dma_len || dma_len + MT_DMA_HDR_LEN > data_len || (dma_len & 0x3)) return -EINVAL; return dma_len; } static struct sk_buff * mt76u_build_rx_skb(struct mt76_dev *dev, void *data, int len, int buf_size) { int head_room, drv_flags = dev->drv->drv_flags; struct sk_buff *skb; head_room = drv_flags & MT_DRV_RX_DMA_HDR ? 0 : MT_DMA_HDR_LEN; if (SKB_WITH_OVERHEAD(buf_size) < head_room + len) { struct page *page; /* slow path, not enough space for data and * skb_shared_info */ skb = alloc_skb(MT_SKB_HEAD_LEN, GFP_ATOMIC); if (!skb) return NULL; skb_put_data(skb, data + head_room, MT_SKB_HEAD_LEN); data += head_room + MT_SKB_HEAD_LEN; page = virt_to_head_page(data); skb_add_rx_frag(skb, skb_shinfo(skb)->nr_frags, page, data - page_address(page), len - MT_SKB_HEAD_LEN, buf_size); return skb; } /* fast path */ skb = build_skb(data, buf_size); if (!skb) return NULL; skb_reserve(skb, head_room); __skb_put(skb, len); return skb; } static int mt76u_process_rx_entry(struct mt76_dev *dev, struct urb *urb, int buf_size) { u8 *data = urb->num_sgs ? sg_virt(&urb->sg[0]) : urb->transfer_buffer; int data_len = urb->num_sgs ? urb->sg[0].length : urb->actual_length; int len, nsgs = 1, head_room, drv_flags = dev->drv->drv_flags; struct sk_buff *skb; if (!test_bit(MT76_STATE_INITIALIZED, &dev->phy.state)) return 0; len = mt76u_get_rx_entry_len(dev, data, urb->actual_length); if (len < 0) return 0; head_room = drv_flags & MT_DRV_RX_DMA_HDR ? 0 : MT_DMA_HDR_LEN; data_len = min_t(int, len, data_len - head_room); if (len == data_len && dev->drv->rx_check && !dev->drv->rx_check(dev, data, data_len)) return 0; skb = mt76u_build_rx_skb(dev, data, data_len, buf_size); if (!skb) return 0; len -= data_len; while (len > 0 && nsgs < urb->num_sgs) { data_len = min_t(int, len, urb->sg[nsgs].length); skb_add_rx_frag(skb, skb_shinfo(skb)->nr_frags, sg_page(&urb->sg[nsgs]), urb->sg[nsgs].offset, data_len, buf_size); len -= data_len; nsgs++; } skb_mark_for_recycle(skb); dev->drv->rx_skb(dev, MT_RXQ_MAIN, skb, NULL); return nsgs; } static void mt76u_complete_rx(struct urb *urb) { struct mt76_dev *dev = dev_get_drvdata(&urb->dev->dev); struct mt76_queue *q = urb->context; unsigned long flags; trace_rx_urb(dev, urb); switch (urb->status) { case -ECONNRESET: case -ESHUTDOWN: case -ENOENT: case -EPROTO: return; default: dev_err_ratelimited(dev->dev, "rx urb failed: %d\n", urb->status); fallthrough; case 0: break; } spin_lock_irqsave(&q->lock, flags); if (WARN_ONCE(q->entry[q->head].urb != urb, "rx urb mismatch")) goto out; q->head = (q->head + 1) % q->ndesc; q->queued++; mt76_worker_schedule(&dev->usb.rx_worker); out: spin_unlock_irqrestore(&q->lock, flags); } static int mt76u_submit_rx_buf(struct mt76_dev *dev, enum mt76_rxq_id qid, struct urb *urb) { int ep = qid == MT_RXQ_MAIN ? MT_EP_IN_PKT_RX : MT_EP_IN_CMD_RESP; mt76u_fill_bulk_urb(dev, USB_DIR_IN, ep, urb, mt76u_complete_rx, &dev->q_rx[qid]); trace_submit_urb(dev, urb); return usb_submit_urb(urb, GFP_ATOMIC); } static void mt76u_process_rx_queue(struct mt76_dev *dev, struct mt76_queue *q) { int qid = q - &dev->q_rx[MT_RXQ_MAIN]; struct urb *urb; int err, count; while (true) { urb = mt76u_get_next_rx_entry(q); if (!urb) break; count = mt76u_process_rx_entry(dev, urb, q->buf_size); if (count > 0) { err = mt76u_refill_rx(dev, q, urb, count); if (err < 0) break; } mt76u_submit_rx_buf(dev, qid, urb); } if (qid == MT_RXQ_MAIN) { local_bh_disable(); mt76_rx_poll_complete(dev, MT_RXQ_MAIN, NULL); local_bh_enable(); } } static void mt76u_rx_worker(struct mt76_worker *w) { struct mt76_usb *usb = container_of(w, struct mt76_usb, rx_worker); struct mt76_dev *dev = container_of(usb, struct mt76_dev, usb); int i; rcu_read_lock(); mt76_for_each_q_rx(dev, i) mt76u_process_rx_queue(dev, &dev->q_rx[i]); rcu_read_unlock(); } static int mt76u_submit_rx_buffers(struct mt76_dev *dev, enum mt76_rxq_id qid) { struct mt76_queue *q = &dev->q_rx[qid]; unsigned long flags; int i, err = 0; spin_lock_irqsave(&q->lock, flags); for (i = 0; i < q->ndesc; i++) { err = mt76u_submit_rx_buf(dev, qid, q->entry[i].urb); if (err < 0) break; } q->head = q->tail = 0; q->queued = 0; spin_unlock_irqrestore(&q->lock, flags); return err; } static int mt76u_alloc_rx_queue(struct mt76_dev *dev, enum mt76_rxq_id qid) { struct mt76_queue *q = &dev->q_rx[qid]; int i, err; err = mt76_create_page_pool(dev, q); if (err) return err; spin_lock_init(&q->lock); q->entry = devm_kcalloc(dev->dev, MT_NUM_RX_ENTRIES, sizeof(*q->entry), GFP_KERNEL); if (!q->entry) return -ENOMEM; q->ndesc = MT_NUM_RX_ENTRIES; q->buf_size = PAGE_SIZE; for (i = 0; i < q->ndesc; i++) { err = mt76u_rx_urb_alloc(dev, q, &q->entry[i]); if (err < 0) return err; } return mt76u_submit_rx_buffers(dev, qid); } int mt76u_alloc_mcu_queue(struct mt76_dev *dev) { return mt76u_alloc_rx_queue(dev, MT_RXQ_MCU); } EXPORT_SYMBOL_GPL(mt76u_alloc_mcu_queue); static void mt76u_free_rx_queue(struct mt76_dev *dev, struct mt76_queue *q) { int i; for (i = 0; i < q->ndesc; i++) { if (!q->entry[i].urb) continue; mt76u_urb_free(q->entry[i].urb); q->entry[i].urb = NULL; } page_pool_destroy(q->page_pool); q->page_pool = NULL; } static void mt76u_free_rx(struct mt76_dev *dev) { int i; mt76_worker_teardown(&dev->usb.rx_worker); mt76_for_each_q_rx(dev, i) mt76u_free_rx_queue(dev, &dev->q_rx[i]); } void mt76u_stop_rx(struct mt76_dev *dev) { int i; mt76_worker_disable(&dev->usb.rx_worker); mt76_for_each_q_rx(dev, i) { struct mt76_queue *q = &dev->q_rx[i]; int j; for (j = 0; j < q->ndesc; j++) usb_poison_urb(q->entry[j].urb); } } EXPORT_SYMBOL_GPL(mt76u_stop_rx); int mt76u_resume_rx(struct mt76_dev *dev) { int i; mt76_for_each_q_rx(dev, i) { struct mt76_queue *q = &dev->q_rx[i]; int err, j; for (j = 0; j < q->ndesc; j++) usb_unpoison_urb(q->entry[j].urb); err = mt76u_submit_rx_buffers(dev, i); if (err < 0) return err; } mt76_worker_enable(&dev->usb.rx_worker); return 0; } EXPORT_SYMBOL_GPL(mt76u_resume_rx); static void mt76u_status_worker(struct mt76_worker *w) { struct mt76_usb *usb = container_of(w, struct mt76_usb, status_worker); struct mt76_dev *dev = container_of(usb, struct mt76_dev, usb); struct mt76_queue_entry entry; struct mt76_queue *q; int i; if (!test_bit(MT76_STATE_RUNNING, &dev->phy.state)) return; for (i = 0; i <= MT_TXQ_PSD; i++) { q = dev->phy.q_tx[i]; if (!q) continue; while (q->queued > 0) { if (!q->entry[q->tail].done) break; entry = q->entry[q->tail]; q->entry[q->tail].done = false; mt76_queue_tx_complete(dev, q, &entry); } if (!q->queued) wake_up(&dev->tx_wait); mt76_worker_schedule(&dev->tx_worker); } if (dev->drv->tx_status_data && !test_and_set_bit(MT76_READING_STATS, &dev->phy.state)) queue_work(dev->wq, &dev->usb.stat_work); } static void mt76u_tx_status_data(struct work_struct *work) { struct mt76_usb *usb; struct mt76_dev *dev; u8 update = 1; u16 count = 0; usb = container_of(work, struct mt76_usb, stat_work); dev = container_of(usb, struct mt76_dev, usb); while (true) { if (test_bit(MT76_REMOVED, &dev->phy.state)) break; if (!dev->drv->tx_status_data(dev, &update)) break; count++; } if (count && test_bit(MT76_STATE_RUNNING, &dev->phy.state)) queue_work(dev->wq, &usb->stat_work); else clear_bit(MT76_READING_STATS, &dev->phy.state); } static void mt76u_complete_tx(struct urb *urb) { struct mt76_dev *dev = dev_get_drvdata(&urb->dev->dev); struct mt76_queue_entry *e = urb->context; if (mt76u_urb_error(urb)) dev_err(dev->dev, "tx urb failed: %d\n", urb->status); e->done = true; mt76_worker_schedule(&dev->usb.status_worker); } static int mt76u_tx_setup_buffers(struct mt76_dev *dev, struct sk_buff *skb, struct urb *urb) { urb->transfer_buffer_length = skb->len; if (!dev->usb.sg_en) { urb->transfer_buffer = skb->data; return 0; } sg_init_table(urb->sg, MT_TX_SG_MAX_SIZE); urb->num_sgs = skb_to_sgvec(skb, urb->sg, 0, skb->len); if (!urb->num_sgs) return -ENOMEM; return urb->num_sgs; } static int mt76u_tx_queue_skb(struct mt76_phy *phy, struct mt76_queue *q, enum mt76_txq_id qid, struct sk_buff *skb, struct mt76_wcid *wcid, struct ieee80211_sta *sta) { struct mt76_tx_info tx_info = { .skb = skb, }; struct mt76_dev *dev = phy->dev; u16 idx = q->head; int err; if (q->queued == q->ndesc) return -ENOSPC; skb->prev = skb->next = NULL; err = dev->drv->tx_prepare_skb(dev, NULL, qid, wcid, sta, &tx_info); if (err < 0) return err; err = mt76u_tx_setup_buffers(dev, tx_info.skb, q->entry[idx].urb); if (err < 0) return err; mt76u_fill_bulk_urb(dev, USB_DIR_OUT, q->ep, q->entry[idx].urb, mt76u_complete_tx, &q->entry[idx]); q->head = (q->head + 1) % q->ndesc; q->entry[idx].skb = tx_info.skb; q->entry[idx].wcid = 0xffff; q->queued++; return idx; } static void mt76u_tx_kick(struct mt76_dev *dev, struct mt76_queue *q) { struct urb *urb; int err; while (q->first != q->head) { urb = q->entry[q->first].urb; trace_submit_urb(dev, urb); err = usb_submit_urb(urb, GFP_ATOMIC); if (err < 0) { if (err == -ENODEV) set_bit(MT76_REMOVED, &dev->phy.state); else dev_err(dev->dev, "tx urb submit failed:%d\n", err); break; } q->first = (q->first + 1) % q->ndesc; } } static void mt76u_ac_to_hwq(struct mt76_dev *dev, struct mt76_queue *q, u8 qid) { u8 ac = qid < IEEE80211_NUM_ACS ? qid : IEEE80211_AC_BE; switch (mt76_chip(dev)) { case 0x7663: { static const u8 lmac_queue_map[] = { /* ac to lmac mapping */ [IEEE80211_AC_BK] = 0, [IEEE80211_AC_BE] = 1, [IEEE80211_AC_VI] = 2, [IEEE80211_AC_VO] = 4, }; q->hw_idx = lmac_queue_map[ac]; q->ep = q->hw_idx + 1; break; } case 0x7961: case 0x7925: q->hw_idx = mt76_ac_to_hwq(ac); q->ep = qid == MT_TXQ_PSD ? MT_EP_OUT_HCCA : q->hw_idx + 1; break; default: q->hw_idx = mt76_ac_to_hwq(ac); q->ep = q->hw_idx + 1; break; } } static int mt76u_alloc_tx(struct mt76_dev *dev) { int i; for (i = 0; i <= MT_TXQ_PSD; i++) { struct mt76_queue *q; int j, err; q = devm_kzalloc(dev->dev, sizeof(*q), GFP_KERNEL); if (!q) return -ENOMEM; spin_lock_init(&q->lock); mt76u_ac_to_hwq(dev, q, i); dev->phy.q_tx[i] = q; q->entry = devm_kcalloc(dev->dev, MT_NUM_TX_ENTRIES, sizeof(*q->entry), GFP_KERNEL); if (!q->entry) return -ENOMEM; q->ndesc = MT_NUM_TX_ENTRIES; for (j = 0; j < q->ndesc; j++) { err = mt76u_urb_alloc(dev, &q->entry[j], MT_TX_SG_MAX_SIZE); if (err < 0) return err; } } return 0; } static void mt76u_free_tx(struct mt76_dev *dev) { int i; mt76_worker_teardown(&dev->usb.status_worker); for (i = 0; i <= MT_TXQ_PSD; i++) { struct mt76_queue *q; int j; q = dev->phy.q_tx[i]; if (!q) continue; for (j = 0; j < q->ndesc; j++) { usb_free_urb(q->entry[j].urb); q->entry[j].urb = NULL; } } } void mt76u_stop_tx(struct mt76_dev *dev) { int ret; mt76_worker_disable(&dev->usb.status_worker); ret = wait_event_timeout(dev->tx_wait, !mt76_has_tx_pending(&dev->phy), HZ / 5); if (!ret) { struct mt76_queue_entry entry; struct mt76_queue *q; int i, j; dev_err(dev->dev, "timed out waiting for pending tx\n"); for (i = 0; i <= MT_TXQ_PSD; i++) { q = dev->phy.q_tx[i]; if (!q) continue; for (j = 0; j < q->ndesc; j++) usb_kill_urb(q->entry[j].urb); } mt76_worker_disable(&dev->tx_worker); /* On device removal we maight queue skb's, but mt76u_tx_kick() * will fail to submit urb, cleanup those skb's manually. */ for (i = 0; i <= MT_TXQ_PSD; i++) { q = dev->phy.q_tx[i]; if (!q) continue; while (q->queued > 0) { entry = q->entry[q->tail]; q->entry[q->tail].done = false; mt76_queue_tx_complete(dev, q, &entry); } } mt76_worker_enable(&dev->tx_worker); } cancel_work_sync(&dev->usb.stat_work); clear_bit(MT76_READING_STATS, &dev->phy.state); mt76_worker_enable(&dev->usb.status_worker); mt76_tx_status_check(dev, true); } EXPORT_SYMBOL_GPL(mt76u_stop_tx); void mt76u_queues_deinit(struct mt76_dev *dev) { mt76u_stop_rx(dev); mt76u_stop_tx(dev); mt76u_free_rx(dev); mt76u_free_tx(dev); } EXPORT_SYMBOL_GPL(mt76u_queues_deinit); int mt76u_alloc_queues(struct mt76_dev *dev) { int err; err = mt76u_alloc_rx_queue(dev, MT_RXQ_MAIN); if (err < 0) return err; return mt76u_alloc_tx(dev); } EXPORT_SYMBOL_GPL(mt76u_alloc_queues); static const struct mt76_queue_ops usb_queue_ops = { .tx_queue_skb = mt76u_tx_queue_skb, .kick = mt76u_tx_kick, }; int __mt76u_init(struct mt76_dev *dev, struct usb_interface *intf, struct mt76_bus_ops *ops) { struct usb_device *udev = interface_to_usbdev(intf); struct mt76_usb *usb = &dev->usb; int err; INIT_WORK(&usb->stat_work, mt76u_tx_status_data); usb->data_len = usb_maxpacket(udev, usb_sndctrlpipe(udev, 0)); if (usb->data_len < 32) usb->data_len = 32; usb->data = devm_kmalloc(dev->dev, usb->data_len, GFP_KERNEL); if (!usb->data) return -ENOMEM; mutex_init(&usb->usb_ctrl_mtx); dev->bus = ops; dev->queue_ops = &usb_queue_ops; dev_set_drvdata(&udev->dev, dev); usb->sg_en = mt76u_check_sg(dev); err = mt76u_set_endpoints(intf, usb); if (err < 0) return err; err = mt76_worker_setup(dev->hw, &usb->rx_worker, mt76u_rx_worker, "usb-rx"); if (err) return err; err = mt76_worker_setup(dev->hw, &usb->status_worker, mt76u_status_worker, "usb-status"); if (err) return err; sched_set_fifo_low(usb->rx_worker.task); sched_set_fifo_low(usb->status_worker.task); return 0; } EXPORT_SYMBOL_GPL(__mt76u_init); int mt76u_init(struct mt76_dev *dev, struct usb_interface *intf) { static struct mt76_bus_ops bus_ops = { .rr = mt76u_rr, .wr = mt76u_wr, .rmw = mt76u_rmw, .read_copy = mt76u_read_copy, .write_copy = mt76u_copy, .wr_rp = mt76u_wr_rp, .rd_rp = mt76u_rd_rp, .type = MT76_BUS_USB, }; return __mt76u_init(dev, intf, &bus_ops); } EXPORT_SYMBOL_GPL(mt76u_init); MODULE_AUTHOR("Lorenzo Bianconi "); MODULE_DESCRIPTION("MediaTek MT76x USB helpers"); MODULE_LICENSE("Dual BSD/GPL");