int mt7601u_mac_start(struct mt7601u_dev *dev) { mt7601u_wr(dev, MT_MAC_SYS_CTRL, MT_MAC_SYS_CTRL_ENABLE_TX); if (!mt76_poll(dev, MT_WPDMA_GLO_CFG, MT_WPDMA_GLO_CFG_TX_DMA_BUSY | MT_WPDMA_GLO_CFG_RX_DMA_BUSY, 0, 200000)) return -ETIMEDOUT; dev->rxfilter = MT_RX_FILTR_CFG_CRC_ERR | MT_RX_FILTR_CFG_PHY_ERR | MT_RX_FILTR_CFG_PROMISC | MT_RX_FILTR_CFG_VER_ERR | MT_RX_FILTR_CFG_DUP | MT_RX_FILTR_CFG_CFACK | MT_RX_FILTR_CFG_CFEND | MT_RX_FILTR_CFG_ACK | MT_RX_FILTR_CFG_CTS | MT_RX_FILTR_CFG_RTS | MT_RX_FILTR_CFG_PSPOLL | MT_RX_FILTR_CFG_BA | MT_RX_FILTR_CFG_CTRL_RSV; mt7601u_wr(dev, MT_RX_FILTR_CFG, dev->rxfilter); mt7601u_wr(dev, MT_MAC_SYS_CTRL, MT_MAC_SYS_CTRL_ENABLE_TX | MT_MAC_SYS_CTRL_ENABLE_RX); if (!mt76_poll(dev, MT_WPDMA_GLO_CFG, MT_WPDMA_GLO_CFG_TX_DMA_BUSY | MT_WPDMA_GLO_CFG_RX_DMA_BUSY, 0, 50)) return -ETIMEDOUT; return 0; }
static void mt7601u_chip_onoff(struct mt7601u_dev *dev, bool enable, bool reset) { u32 val; mutex_lock(&dev->hw_atomic_mutex); val = mt7601u_rr(dev, MT_WLAN_FUN_CTRL); if (reset) { val |= MT_WLAN_FUN_CTRL_GPIO_OUT_EN; val &= ~MT_WLAN_FUN_CTRL_FRC_WL_ANT_SEL; if (val & MT_WLAN_FUN_CTRL_WLAN_EN) { val |= (MT_WLAN_FUN_CTRL_WLAN_RESET | MT_WLAN_FUN_CTRL_WLAN_RESET_RF); mt7601u_wr(dev, MT_WLAN_FUN_CTRL, val); udelay(20); val &= ~(MT_WLAN_FUN_CTRL_WLAN_RESET | MT_WLAN_FUN_CTRL_WLAN_RESET_RF); } } mt7601u_wr(dev, MT_WLAN_FUN_CTRL, val); udelay(20); mt7601u_set_wlan_state(dev, val, enable); mutex_unlock(&dev->hw_atomic_mutex); }
static void mt7601u_reset_csr_bbp(struct mt7601u_dev *dev) { mt7601u_wr(dev, MT_MAC_SYS_CTRL, (MT_MAC_SYS_CTRL_RESET_CSR | MT_MAC_SYS_CTRL_RESET_BBP)); mt7601u_wr(dev, MT_USB_DMA_CFG, 0); msleep(1); mt7601u_wr(dev, MT_MAC_SYS_CTRL, 0); }
static void mt7601u_bss_info_changed(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ieee80211_bss_conf *info, u32 changed) { struct mt7601u_dev *dev = hw->priv; mutex_lock(&dev->mutex); if (changed & BSS_CHANGED_ASSOC) mt7601u_phy_con_cal_onoff(dev, info); if (changed & BSS_CHANGED_BSSID) { mt7601u_addr_wr(dev, MT_MAC_BSSID_DW0, info->bssid); /* Note: this is a hack because beacon_int is not changed * on leave nor is any more appropriate event generated. * rt2x00 doesn't seem to be bothered though. */ if (is_zero_ether_addr(info->bssid)) mt7601u_mac_config_tsf(dev, false, 0); } if (changed & BSS_CHANGED_BASIC_RATES) { mt7601u_wr(dev, MT_LEGACY_BASIC_RATE, info->basic_rates); mt7601u_wr(dev, MT_HT_FBK_CFG0, 0x65432100); mt7601u_wr(dev, MT_HT_FBK_CFG1, 0xedcba980); mt7601u_wr(dev, MT_LG_FBK_CFG0, 0xedcba988); mt7601u_wr(dev, MT_LG_FBK_CFG1, 0x00002100); } if (changed & BSS_CHANGED_BEACON_INT) mt7601u_mac_config_tsf(dev, true, info->beacon_int); if (changed & BSS_CHANGED_HT || changed & BSS_CHANGED_ERP_CTS_PROT) mt7601u_mac_set_protection(dev, info->use_cts_prot, info->ht_operation_mode); if (changed & BSS_CHANGED_ERP_PREAMBLE) mt7601u_mac_set_short_preamble(dev, info->use_short_preamble); if (changed & BSS_CHANGED_ERP_SLOT) { int slottime = info->use_short_slot ? 9 : 20; mt76_rmw_field(dev, MT_BKOFF_SLOT_CFG, MT_BKOFF_SLOT_CFG_SLOTTIME, slottime); } if (changed & BSS_CHANGED_ASSOC) mt7601u_phy_recalibrate_after_assoc(dev); mutex_unlock(&dev->mutex); }
u32 mt7601u_rmc(struct mt7601u_dev *dev, u32 offset, u32 mask, u32 val) { u32 reg = mt7601u_rr(dev, offset); val |= reg & ~mask; if (reg != val) mt7601u_wr(dev, offset, val); return val; }
static void mt7601u_init_usb_dma(struct mt7601u_dev *dev) { u32 val; val = FIELD_PREP(MT_USB_DMA_CFG_RX_BULK_AGG_TOUT, MT_USB_AGGR_TIMEOUT) | FIELD_PREP(MT_USB_DMA_CFG_RX_BULK_AGG_LMT, MT_USB_AGGR_SIZE_LIMIT) | MT_USB_DMA_CFG_RX_BULK_EN | MT_USB_DMA_CFG_TX_BULK_EN; if (dev->in_max_packet == 512) val |= MT_USB_DMA_CFG_RX_BULK_AGG_EN; mt7601u_wr(dev, MT_USB_DMA_CFG, val); val |= MT_USB_DMA_CFG_UDMA_RX_WL_DROP; mt7601u_wr(dev, MT_USB_DMA_CFG, val); val &= ~MT_USB_DMA_CFG_UDMA_RX_WL_DROP; mt7601u_wr(dev, MT_USB_DMA_CFG, val); }
static int __mt7601u_dma_fw(struct mt7601u_dev *dev, const struct mt7601u_dma_buf *dma_buf, const void *data, u32 len, u32 dst_addr) { DECLARE_COMPLETION_ONSTACK(cmpl); struct mt7601u_dma_buf buf = *dma_buf; /* we need to fake length */ __le32 reg; u32 val; int ret; reg = cpu_to_le32(MT76_SET(MT_TXD_INFO_TYPE, DMA_PACKET) | MT76_SET(MT_TXD_INFO_D_PORT, CPU_TX_PORT) | MT76_SET(MT_TXD_INFO_LEN, len)); memcpy(buf.buf, ®, sizeof(reg)); memcpy(buf.buf + sizeof(reg), data, len); memset(buf.buf + sizeof(reg) + len, 0, 8); ret = mt7601u_vendor_single_wr(dev, MT_VEND_WRITE_FCE, MT_FCE_DMA_ADDR, dst_addr); if (ret) return ret; len = roundup(len, 4); ret = mt7601u_vendor_single_wr(dev, MT_VEND_WRITE_FCE, MT_FCE_DMA_LEN, len << 16); if (ret) return ret; buf.len = MT_DMA_HDR_LEN + len + 4; ret = mt7601u_usb_submit_buf(dev, USB_DIR_OUT, MT_EP_OUT_INBAND_CMD, &buf, GFP_KERNEL, mt7601u_complete_urb, &cmpl); if (ret) return ret; if (!wait_for_completion_timeout(&cmpl, msecs_to_jiffies(1000))) { dev_err(dev->dev, "Error: firmware upload timed out\n"); usb_kill_urb(buf.urb); return -ETIMEDOUT; } if (mt7601u_urb_has_error(buf.urb)) { dev_err(dev->dev, "Error: firmware upload urb failed:%d\n", buf.urb->status); return buf.urb->status; } val = mt7601u_rr(dev, MT_TX_CPU_FROM_FCE_CPU_DESC_IDX); val++; mt7601u_wr(dev, MT_TX_CPU_FROM_FCE_CPU_DESC_IDX, val); return 0; }
static void mt76_init_beacon_offsets(struct mt7601u_dev *dev) { u16 base = MT_BEACON_BASE; u32 regs[4] = {}; int i; for (i = 0; i < 16; i++) { u16 addr = dev->beacon_offsets[i]; regs[i / 4] |= ((addr - base) / 64) << (8 * (i % 4)); } for (i = 0; i < 4; i++) mt7601u_wr(dev, MT_BCN_OFFSET(i), regs[i]); }
static void mt7601u_set_wlan_state(struct mt7601u_dev *dev, u32 val, bool enable) { int i; /* Note: we don't turn off WLAN_CLK because that makes the device * not respond properly on the probe path. * In case anyone (PSM?) wants to use this function we can * bring the clock stuff back and fixup the probe path. */ if (enable) val |= (MT_WLAN_FUN_CTRL_WLAN_EN | MT_WLAN_FUN_CTRL_WLAN_CLK_EN); else val &= ~(MT_WLAN_FUN_CTRL_WLAN_EN); mt7601u_wr(dev, MT_WLAN_FUN_CTRL, val); udelay(20); if (enable) { set_bit(MT7601U_STATE_WLAN_RUNNING, &dev->state); } else { clear_bit(MT7601U_STATE_WLAN_RUNNING, &dev->state); return; } for (i = 200; i; i--) { val = mt7601u_rr(dev, MT_CMB_CTRL); if (val & MT_CMB_CTRL_XTAL_RDY && val & MT_CMB_CTRL_PLL_LD) break; udelay(20); } /* Note: vendor driver tries to disable/enable wlan here and retry * but the code which does it is so buggy it must have never * triggered, so don't bother. */ if (!i) dev_err(dev->dev, "Error: PLL and XTAL check failed!\n"); }
static int mt7601u_write_mac_initvals(struct mt7601u_dev *dev) { int ret; ret = mt7601u_write_reg_pairs(dev, MT_MCU_MEMMAP_WLAN, mac_common_vals, ARRAY_SIZE(mac_common_vals)); if (ret) return ret; ret = mt7601u_write_reg_pairs(dev, MT_MCU_MEMMAP_WLAN, mac_chip_vals, ARRAY_SIZE(mac_chip_vals)); if (ret) return ret; mt76_init_beacon_offsets(dev); mt7601u_wr(dev, MT_AUX_CLK_CFG, 0); return 0; }
void mt7601u_addr_wr(struct mt7601u_dev *dev, const u32 offset, const u8 *addr) { mt7601u_wr(dev, offset, get_unaligned_le32(addr)); mt7601u_wr(dev, offset + 4, addr[4] | addr[5] << 8); }
u32 mt7601u_rmw(struct mt7601u_dev *dev, u32 offset, u32 mask, u32 val) { val |= mt7601u_rr(dev, offset) & ~mask; mt7601u_wr(dev, offset, val); return val; }
int mt7601u_init_hardware(struct mt7601u_dev *dev) { static const u16 beacon_offsets[16] = { /* 512 byte per beacon */ 0xc000, 0xc200, 0xc400, 0xc600, 0xc800, 0xca00, 0xcc00, 0xce00, 0xd000, 0xd200, 0xd400, 0xd600, 0xd800, 0xda00, 0xdc00, 0xde00 }; int ret; dev->beacon_offsets = beacon_offsets; mt7601u_chip_onoff(dev, true, false); ret = mt7601u_wait_asic_ready(dev); if (ret) goto err; ret = mt7601u_mcu_init(dev); if (ret) goto err; if (!mt76_poll_msec(dev, MT_WPDMA_GLO_CFG, MT_WPDMA_GLO_CFG_TX_DMA_BUSY | MT_WPDMA_GLO_CFG_RX_DMA_BUSY, 0, 100)) { ret = -EIO; goto err; } /* Wait for ASIC ready after FW load. */ ret = mt7601u_wait_asic_ready(dev); if (ret) goto err; mt7601u_reset_csr_bbp(dev); mt7601u_init_usb_dma(dev); ret = mt7601u_mcu_cmd_init(dev); if (ret) goto err; ret = mt7601u_dma_init(dev); if (ret) goto err_mcu; ret = mt7601u_write_mac_initvals(dev); if (ret) goto err_rx; if (!mt76_poll_msec(dev, MT_MAC_STATUS, MT_MAC_STATUS_TX | MT_MAC_STATUS_RX, 0, 100)) { ret = -EIO; goto err_rx; } ret = mt7601u_init_bbp(dev); if (ret) goto err_rx; ret = mt7601u_init_wcid_mem(dev); if (ret) goto err_rx; ret = mt7601u_init_key_mem(dev); if (ret) goto err_rx; ret = mt7601u_init_wcid_attr_mem(dev); if (ret) goto err_rx; mt76_clear(dev, MT_BEACON_TIME_CFG, (MT_BEACON_TIME_CFG_TIMER_EN | MT_BEACON_TIME_CFG_SYNC_MODE | MT_BEACON_TIME_CFG_TBTT_EN | MT_BEACON_TIME_CFG_BEACON_TX)); mt7601u_reset_counters(dev); mt7601u_rmw(dev, MT_US_CYC_CFG, MT_US_CYC_CNT, 0x1e); mt7601u_wr(dev, MT_TXOP_CTRL_CFG, FIELD_PREP(MT_TXOP_TRUN_EN, 0x3f) | FIELD_PREP(MT_TXOP_EXT_CCA_DLY, 0x58)); ret = mt7601u_eeprom_init(dev); if (ret) goto err_rx; ret = mt7601u_phy_init(dev); if (ret) goto err_rx; mt7601u_set_rx_path(dev, 0); mt7601u_set_tx_dac(dev, 0); mt7601u_mac_set_ctrlch(dev, false); mt7601u_bbp_set_ctrlch(dev, false); mt7601u_bbp_set_bw(dev, MT_BW_20); return 0; err_rx: mt7601u_dma_cleanup(dev); err_mcu: mt7601u_mcu_cmd_deinit(dev); err: mt7601u_chip_onoff(dev, false, false); return ret; }
static int mt7601u_load_firmware(struct mt7601u_dev *dev) { const struct firmware *fw; const struct mt76_fw_header *hdr; int len, ret; u32 val; mt7601u_wr(dev, MT_USB_DMA_CFG, (MT_USB_DMA_CFG_RX_BULK_EN | MT_USB_DMA_CFG_TX_BULK_EN)); if (firmware_running(dev)) return 0; ret = request_firmware(&fw, MT7601U_FIRMWARE, dev->dev); if (ret) return ret; if (!fw || !fw->data || fw->size < sizeof(*hdr)) goto err_inv_fw; hdr = (const struct mt76_fw_header *) fw->data; if (le32_to_cpu(hdr->ilm_len) <= MT_MCU_IVB_SIZE) goto err_inv_fw; len = sizeof(*hdr); len += le32_to_cpu(hdr->ilm_len); len += le32_to_cpu(hdr->dlm_len); if (fw->size != len) goto err_inv_fw; val = le16_to_cpu(hdr->fw_ver); dev_info(dev->dev, "Firmware Version: %d.%d.%02d Build: %x Build time: %.16s\n", (val >> 12) & 0xf, (val >> 8) & 0xf, val & 0xf, le16_to_cpu(hdr->build_ver), hdr->build_time); len = le32_to_cpu(hdr->ilm_len); mt7601u_wr(dev, 0x94c, 0); mt7601u_wr(dev, MT_FCE_PSE_CTRL, 0); mt7601u_vendor_reset(dev); msleep(5); mt7601u_wr(dev, 0xa44, 0); mt7601u_wr(dev, 0x230, 0x84210); mt7601u_wr(dev, 0x400, 0x80c00); mt7601u_wr(dev, 0x800, 1); mt7601u_rmw(dev, MT_PBF_CFG, 0, (MT_PBF_CFG_TX0Q_EN | MT_PBF_CFG_TX1Q_EN | MT_PBF_CFG_TX2Q_EN | MT_PBF_CFG_TX3Q_EN)); mt7601u_wr(dev, MT_FCE_PSE_CTRL, 1); mt7601u_wr(dev, MT_USB_DMA_CFG, (MT_USB_DMA_CFG_RX_BULK_EN | MT_USB_DMA_CFG_TX_BULK_EN)); val = mt76_set(dev, MT_USB_DMA_CFG, MT_USB_DMA_CFG_TX_CLR); val &= ~MT_USB_DMA_CFG_TX_CLR; mt7601u_wr(dev, MT_USB_DMA_CFG, val); /* FCE tx_fs_base_ptr */ mt7601u_wr(dev, MT_TX_CPU_FROM_FCE_BASE_PTR, 0x400230); /* FCE tx_fs_max_cnt */ mt7601u_wr(dev, MT_TX_CPU_FROM_FCE_MAX_COUNT, 1); /* FCE pdma enable */ mt7601u_wr(dev, MT_FCE_PDMA_GLOBAL_CONF, 0x44); /* FCE skip_fs_en */ mt7601u_wr(dev, MT_FCE_SKIP_FS, 3); ret = mt7601u_upload_firmware(dev, (const struct mt76_fw *)fw->data); release_firmware(fw); return ret; err_inv_fw: dev_err(dev->dev, "Invalid firmware image\n"); release_firmware(fw); return -ENOENT; }