// SPDX-License-Identifier: ISC
/* Copyright (C) 2019 MediaTek Inc.
*
* Author: Roy Luo <royluo@google.com>
* Ryder Lee <ryder.lee@mediatek.com>
* Felix Fietkau <nbd@nbd.name>
*/
#include <linux/etherdevice.h>
#include <linux/platform_device.h>
#include <linux/pci.h>
#include <linux/module.h>
#include "mt7615.h"
#include "mcu.h"
static bool mt7615_dev_running(struct mt7615_dev *dev)
{
struct mt7615_phy *phy;
if (test_bit(MT76_STATE_RUNNING, &dev->mphy.state))
return true;
phy = mt7615_ext_phy(dev);
return phy && test_bit(MT76_STATE_RUNNING, &phy->mt76->state);
}
static int mt7615_start(struct ieee80211_hw *hw)
{
struct mt7615_dev *dev = mt7615_hw_dev(hw);
struct mt7615_phy *phy = mt7615_hw_phy(hw);
bool running;
if (!mt7615_wait_for_mcu_init(dev))
return -EIO;
mutex_lock(&dev->mt76.mutex);
running = mt7615_dev_running(dev);
if (!running) {
mt7615_mcu_set_pm(dev, 0, 0);
mt7615_mcu_set_mac_enable(dev, 0, true);
mt7615_mac_enable_nf(dev, 0);
}
if (phy != &dev->phy) {
mt7615_mcu_set_pm(dev, 1, 0);
mt7615_mcu_set_mac_enable(dev, 1, true);
mt7615_mac_enable_nf(dev, 1);
}
mt7615_mcu_set_channel_domain(phy);
mt7615_mcu_set_chan_info(phy, MCU_EXT_CMD_SET_RX_PATH);
set_bit(MT76_STATE_RUNNING, &phy->mt76->state);
if (running)
goto out;
mt7615_mac_reset_counters(dev);
ieee80211_queue_delayed_work(mt76_hw(dev), &dev->mt76.mac_work,
MT7615_WATCHDOG_TIME);
out:
mutex_unlock(&dev->mt76.mutex);
return 0;
}
static void mt7615_stop(struct ieee80211_hw *hw)
{
struct mt7615_dev *dev = mt7615_hw_dev(hw);
struct mt7615_phy *phy = mt7615_hw_phy(hw);
mutex_lock(&dev->mt76.mutex);
clear_bit(MT76_STATE_RUNNING, &phy->mt76->state);
cancel_delayed_work_sync(&phy->scan_work);
if (phy != &dev->phy) {
mt7615_mcu_set_pm(dev, 1, 1);
mt7615_mcu_set_mac_enable(dev, 1, false);
}
if (!mt7615_dev_running(dev)) {
cancel_delayed_work_sync(&dev->mt76.mac_work);
mt7615_mcu_set_pm(dev, 0, 1);
mt7615_mcu_set_mac_enable(dev, 0, false);
}
mutex_unlock(&dev->mt76.mutex);
}
static int get_omac_idx(enum nl80211_iftype type, u32 mask)
{
int i;
switch (type) {
case NL80211_IFTYPE_MONITOR:
case NL80211_IFTYPE_AP:
case NL80211_IFTYPE_MESH_POINT:
case NL80211_IFTYPE_ADHOC:
/* ap use hw bssid 0 and ext bssid */
if (~mask & BIT(HW_BSSID_0))
return HW_BSSID_0;
for (i = EXT_BSSID_1; i < EXT_BSSID_END; i++)
if (~mask & BIT(i))
return i;
break;
case NL80211_IFTYPE_STATION:
/* sta use hw bssid other than 0 */
for (i = HW_BSSID_1; i < HW_BSSID_MAX; i++)
if (~mask & BIT(i))
return i;
break;
default:
WARN_ON(1);
break;
}
return -1;
}
static int mt7615_add_interface(struct ieee80211_hw *hw,
struct ieee80211_vif *vif)
{
struct mt7615_vif *mvif = (struct mt7615_vif *)vif->drv_priv;
struct mt7615_dev *dev = mt7615_hw_dev(hw);
struct mt7615_phy *phy = mt7615_hw_phy(hw);
struct mt76_txq *mtxq;
bool ext_phy = phy != &dev->phy;
int idx, ret = 0;
mutex_lock(&dev->mt76.mutex);
mvif->idx = ffs(~dev->vif_mask) - 1;
if (mvif->idx >= MT7615_MAX_INTERFACES) {
ret = -ENOSPC;
goto out;
}
idx = get_omac_idx(vif->type, dev->omac_mask);
if (idx < 0) {
ret = -ENOSPC;
goto out;
}
mvif->omac_idx = idx;
mvif->band_idx = ext_phy;
if (mt7615_ext_phy(dev))
mvif->wmm_idx = ext_phy * (MT7615_MAX_WMM_SETS / 2) +
mvif->idx % (MT7615_MAX_WMM_SETS / 2);
else
mvif->wmm_idx = mvif->idx % MT7615_MAX_WMM_SETS;
ret = mt7615_mcu_add_dev_info(dev, vif, true);
if (ret)
goto out<