summaryrefslogtreecommitdiff
path: root/drivers/net/wireless/realtek/rtw88/main.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/net/wireless/realtek/rtw88/main.c')
-rw-r--r--drivers/net/wireless/realtek/rtw88/main.c157
1 files changed, 154 insertions, 3 deletions
diff --git a/drivers/net/wireless/realtek/rtw88/main.c b/drivers/net/wireless/realtek/rtw88/main.c
index b2e78737bd5d..5bf6b4581557 100644
--- a/drivers/net/wireless/realtek/rtw88/main.c
+++ b/drivers/net/wireless/realtek/rtw88/main.c
@@ -18,6 +18,7 @@
#include "debug.h"
#include "bf.h"
#include "sar.h"
+#include "sdio.h"
bool rtw_disable_lps_deep_mode;
EXPORT_SYMBOL(rtw_disable_lps_deep_mode);
@@ -102,6 +103,26 @@ static struct ieee80211_rate rtw_ratetable[] = {
{.bitrate = 540, .hw_value = 0x0b,},
};
+static const struct ieee80211_iface_limit rtw_iface_limits[] = {
+ {
+ .max = 1,
+ .types = BIT(NL80211_IFTYPE_STATION),
+ },
+ {
+ .max = 1,
+ .types = BIT(NL80211_IFTYPE_AP),
+ }
+};
+
+static const struct ieee80211_iface_combination rtw_iface_combs[] = {
+ {
+ .limits = rtw_iface_limits,
+ .n_limits = ARRAY_SIZE(rtw_iface_limits),
+ .max_interfaces = 2,
+ .num_different_channels = 1,
+ }
+};
+
u16 rtw_desc_to_bitrate(u8 desc_rate)
{
struct ieee80211_rate rate;
@@ -256,7 +277,7 @@ static void rtw_watch_dog_work(struct work_struct *work)
* threshold.
*/
if (rtwdev->ps_enabled && data.rtwvif && !ps_active &&
- !rtwdev->beacon_loss)
+ !rtwdev->beacon_loss && !rtwdev->ap_active)
rtw_enter_lps(rtwdev, data.rtwvif->port);
rtwdev->watch_dog_cnt++;
@@ -609,6 +630,7 @@ free:
rcu_read_unlock();
rtw_iterate_stas_atomic(rtwdev, rtw_reset_sta_iter, rtwdev);
rtw_iterate_vifs_atomic(rtwdev, rtw_reset_vif_iter, rtwdev);
+ bitmap_zero(rtwdev->hw_port, RTW_PORT_NUM);
rtw_enter_ips(rtwdev);
}
@@ -828,6 +850,9 @@ void rtw_set_channel(struct rtw_dev *rtwdev)
rtw_update_channel(rtwdev, center_chan, primary_chan, band, bandwidth);
+ if (rtwdev->scan_info.op_chan)
+ rtw_store_op_chan(rtwdev, true);
+
chip->ops->set_channel(rtwdev, center_chan, bandwidth,
hal->current_primary_channel_index);
@@ -1785,6 +1810,10 @@ static int rtw_chip_parameter_setup(struct rtw_dev *rtwdev)
rtwdev->hci.rpwm_addr = 0x03d9;
rtwdev->hci.cpwm_addr = 0x03da;
break;
+ case RTW_HCI_TYPE_SDIO:
+ rtwdev->hci.rpwm_addr = REG_SDIO_HRPWM1;
+ rtwdev->hci.cpwm_addr = REG_SDIO_HCPWM1_V2;
+ break;
case RTW_HCI_TYPE_USB:
rtwdev->hci.rpwm_addr = 0xfe58;
rtwdev->hci.cpwm_addr = 0xfe57;
@@ -1979,7 +2008,7 @@ static int rtw_chip_board_info_setup(struct rtw_dev *rtwdev)
if (!rfe_def)
return -ENODEV;
- rtw_phy_setup_phy_cond(rtwdev, 0);
+ rtw_phy_setup_phy_cond(rtwdev, hal->pkg_type);
rtw_phy_init_tx_power(rtwdev);
if (rfe_def->agc_btg_tbl)
@@ -2158,9 +2187,11 @@ int rtw_register_hw(struct rtw_dev *rtwdev, struct ieee80211_hw *hw)
int max_tx_headroom = 0;
int ret;
- /* TODO: USB & SDIO may need extra room? */
max_tx_headroom = rtwdev->chip->tx_pkt_desc_sz;
+ if (rtw_hci_type(rtwdev) == RTW_HCI_TYPE_SDIO)
+ max_tx_headroom += RTW_SDIO_DATA_PTR_ALIGN;
+
hw->extra_tx_headroom = max_tx_headroom;
hw->queues = IEEE80211_NUM_ACS;
hw->txq_data_size = sizeof(struct rtw_txq);
@@ -2194,6 +2225,11 @@ int rtw_register_hw(struct rtw_dev *rtwdev, struct ieee80211_hw *hw)
hw->wiphy->max_scan_ssids = RTW_SCAN_MAX_SSIDS;
hw->wiphy->max_scan_ie_len = rtw_get_max_scan_ie_len(rtwdev);
+ if (rtwdev->chip->id == RTW_CHIP_TYPE_8822C) {
+ hw->wiphy->iface_combinations = rtw_iface_combs;
+ hw->wiphy->n_iface_combinations = ARRAY_SIZE(rtw_iface_combs);
+ }
+
wiphy_ext_feature_set(hw->wiphy, NL80211_EXT_FEATURE_CAN_REPLACE_PTK0);
wiphy_ext_feature_set(hw->wiphy, NL80211_EXT_FEATURE_SCAN_RANDOM_SN);
wiphy_ext_feature_set(hw->wiphy, NL80211_EXT_FEATURE_SET_SCAN_DWELL);
@@ -2243,6 +2279,121 @@ void rtw_unregister_hw(struct rtw_dev *rtwdev, struct ieee80211_hw *hw)
}
EXPORT_SYMBOL(rtw_unregister_hw);
+static
+void rtw_swap_reg_nbytes(struct rtw_dev *rtwdev, const struct rtw_hw_reg *reg1,
+ const struct rtw_hw_reg *reg2, u8 nbytes)
+{
+ u8 i;
+
+ for (i = 0; i < nbytes; i++) {
+ u8 v1 = rtw_read8(rtwdev, reg1->addr + i);
+ u8 v2 = rtw_read8(rtwdev, reg2->addr + i);
+
+ rtw_write8(rtwdev, reg1->addr + i, v2);
+ rtw_write8(rtwdev, reg2->addr + i, v1);
+ }
+}
+
+static
+void rtw_swap_reg_mask(struct rtw_dev *rtwdev, const struct rtw_hw_reg *reg1,
+ const struct rtw_hw_reg *reg2)
+{
+ u32 v1, v2;
+
+ v1 = rtw_read32_mask(rtwdev, reg1->addr, reg1->mask);
+ v2 = rtw_read32_mask(rtwdev, reg2->addr, reg2->mask);
+ rtw_write32_mask(rtwdev, reg2->addr, reg2->mask, v1);
+ rtw_write32_mask(rtwdev, reg1->addr, reg1->mask, v2);
+}
+
+struct rtw_iter_port_switch_data {
+ struct rtw_dev *rtwdev;
+ struct rtw_vif *rtwvif_ap;
+};
+
+static void rtw_port_switch_iter(void *data, u8 *mac, struct ieee80211_vif *vif)
+{
+ struct rtw_iter_port_switch_data *iter_data = data;
+ struct rtw_dev *rtwdev = iter_data->rtwdev;
+ struct rtw_vif *rtwvif_target = (struct rtw_vif *)vif->drv_priv;
+ struct rtw_vif *rtwvif_ap = iter_data->rtwvif_ap;
+ const struct rtw_hw_reg *reg1, *reg2;
+
+ if (rtwvif_target->port != RTW_PORT_0)
+ return;
+
+ rtw_dbg(rtwdev, RTW_DBG_STATE, "AP port switch from %d -> %d\n",
+ rtwvif_ap->port, rtwvif_target->port);
+
+ reg1 = &rtwvif_ap->conf->net_type;
+ reg2 = &rtwvif_target->conf->net_type;
+ rtw_swap_reg_mask(rtwdev, reg1, reg2);
+
+ reg1 = &rtwvif_ap->conf->mac_addr;
+ reg2 = &rtwvif_target->conf->mac_addr;
+ rtw_swap_reg_nbytes(rtwdev, reg1, reg2, ETH_ALEN);
+
+ reg1 = &rtwvif_ap->conf->bssid;
+ reg2 = &rtwvif_target->conf->bssid;
+ rtw_swap_reg_nbytes(rtwdev, reg1, reg2, ETH_ALEN);
+
+ reg1 = &rtwvif_ap->conf->bcn_ctrl;
+ reg2 = &rtwvif_target->conf->bcn_ctrl;
+ rtw_swap_reg_nbytes(rtwdev, reg1, reg2, 1);
+
+ swap(rtwvif_target->port, rtwvif_ap->port);
+ swap(rtwvif_target->conf, rtwvif_ap->conf);
+}
+
+void rtw_core_port_switch(struct rtw_dev *rtwdev, struct ieee80211_vif *vif)
+{
+ struct rtw_vif *rtwvif = (struct rtw_vif *)vif->drv_priv;
+ struct rtw_iter_port_switch_data iter_data;
+
+ if (vif->type != NL80211_IFTYPE_AP || rtwvif->port == RTW_PORT_0)
+ return;
+
+ iter_data.rtwdev = rtwdev;
+ iter_data.rtwvif_ap = rtwvif;
+ rtw_iterate_vifs(rtwdev, rtw_port_switch_iter, &iter_data);
+}
+
+static void rtw_check_sta_active_iter(void *data, u8 *mac,
+ struct ieee80211_vif *vif)
+{
+ struct rtw_vif *rtwvif = (struct rtw_vif *)vif->drv_priv;
+ bool *active = data;
+
+ if (*active)
+ return;
+
+ if (vif->type != NL80211_IFTYPE_STATION)
+ return;
+
+ if (vif->cfg.assoc || !is_zero_ether_addr(rtwvif->bssid))
+ *active = true;
+}
+
+bool rtw_core_check_sta_active(struct rtw_dev *rtwdev)
+{
+ bool sta_active = false;
+
+ rtw_iterate_vifs(rtwdev, rtw_check_sta_active_iter, &sta_active);
+
+ return rtwdev->ap_active || sta_active;
+}
+
+void rtw_core_enable_beacon(struct rtw_dev *rtwdev, bool enable)
+{
+ if (!rtwdev->ap_active)
+ return;
+
+ if (enable)
+ rtw_write32_set(rtwdev, REG_BCN_CTRL, BIT_EN_BCN_FUNCTION);
+ else
+ rtw_write32_clr(rtwdev, REG_BCN_CTRL, BIT_EN_BCN_FUNCTION);
+}
+
MODULE_AUTHOR("Realtek Corporation");
MODULE_DESCRIPTION("Realtek 802.11ac wireless core module");
MODULE_LICENSE("Dual BSD/GPL");