162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * RTL8XXXU mac80211 USB driver - 8723a specific subdriver 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (c) 2014 - 2017 Jes Sorensen <Jes.Sorensen@gmail.com> 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * Portions, notably calibration code: 862306a36Sopenharmony_ci * Copyright(c) 2007 - 2011 Realtek Corporation. All rights reserved. 962306a36Sopenharmony_ci * 1062306a36Sopenharmony_ci * This driver was written as a replacement for the vendor provided 1162306a36Sopenharmony_ci * rtl8723au driver. As the Realtek 8xxx chips are very similar in 1262306a36Sopenharmony_ci * their programming interface, I have started adding support for 1362306a36Sopenharmony_ci * additional 8xxx chips like the 8192cu, 8188cus, etc. 1462306a36Sopenharmony_ci */ 1562306a36Sopenharmony_ci 1662306a36Sopenharmony_ci#include <linux/init.h> 1762306a36Sopenharmony_ci#include <linux/kernel.h> 1862306a36Sopenharmony_ci#include <linux/sched.h> 1962306a36Sopenharmony_ci#include <linux/errno.h> 2062306a36Sopenharmony_ci#include <linux/slab.h> 2162306a36Sopenharmony_ci#include <linux/module.h> 2262306a36Sopenharmony_ci#include <linux/spinlock.h> 2362306a36Sopenharmony_ci#include <linux/list.h> 2462306a36Sopenharmony_ci#include <linux/usb.h> 2562306a36Sopenharmony_ci#include <linux/netdevice.h> 2662306a36Sopenharmony_ci#include <linux/etherdevice.h> 2762306a36Sopenharmony_ci#include <linux/ethtool.h> 2862306a36Sopenharmony_ci#include <linux/wireless.h> 2962306a36Sopenharmony_ci#include <linux/firmware.h> 3062306a36Sopenharmony_ci#include <linux/moduleparam.h> 3162306a36Sopenharmony_ci#include <net/mac80211.h> 3262306a36Sopenharmony_ci#include "rtl8xxxu.h" 3362306a36Sopenharmony_ci#include "rtl8xxxu_regs.h" 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_cistatic struct rtl8xxxu_power_base rtl8723a_power_base = { 3662306a36Sopenharmony_ci .reg_0e00 = 0x0a0c0c0c, 3762306a36Sopenharmony_ci .reg_0e04 = 0x02040608, 3862306a36Sopenharmony_ci .reg_0e08 = 0x00000000, 3962306a36Sopenharmony_ci .reg_086c = 0x00000000, 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_ci .reg_0e10 = 0x0a0c0d0e, 4262306a36Sopenharmony_ci .reg_0e14 = 0x02040608, 4362306a36Sopenharmony_ci .reg_0e18 = 0x0a0c0d0e, 4462306a36Sopenharmony_ci .reg_0e1c = 0x02040608, 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_ci .reg_0830 = 0x0a0c0c0c, 4762306a36Sopenharmony_ci .reg_0834 = 0x02040608, 4862306a36Sopenharmony_ci .reg_0838 = 0x00000000, 4962306a36Sopenharmony_ci .reg_086c_2 = 0x00000000, 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_ci .reg_083c = 0x0a0c0d0e, 5262306a36Sopenharmony_ci .reg_0848 = 0x02040608, 5362306a36Sopenharmony_ci .reg_084c = 0x0a0c0d0e, 5462306a36Sopenharmony_ci .reg_0868 = 0x02040608, 5562306a36Sopenharmony_ci}; 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_cistatic const struct rtl8xxxu_rfregval rtl8723au_radioa_1t_init_table[] = { 5862306a36Sopenharmony_ci {0x00, 0x00030159}, {0x01, 0x00031284}, 5962306a36Sopenharmony_ci {0x02, 0x00098000}, {0x03, 0x00039c63}, 6062306a36Sopenharmony_ci {0x04, 0x000210e7}, {0x09, 0x0002044f}, 6162306a36Sopenharmony_ci {0x0a, 0x0001a3f1}, {0x0b, 0x00014787}, 6262306a36Sopenharmony_ci {0x0c, 0x000896fe}, {0x0d, 0x0000e02c}, 6362306a36Sopenharmony_ci {0x0e, 0x00039ce7}, {0x0f, 0x00000451}, 6462306a36Sopenharmony_ci {0x19, 0x00000000}, {0x1a, 0x00030355}, 6562306a36Sopenharmony_ci {0x1b, 0x00060a00}, {0x1c, 0x000fc378}, 6662306a36Sopenharmony_ci {0x1d, 0x000a1250}, {0x1e, 0x0000024f}, 6762306a36Sopenharmony_ci {0x1f, 0x00000000}, {0x20, 0x0000b614}, 6862306a36Sopenharmony_ci {0x21, 0x0006c000}, {0x22, 0x00000000}, 6962306a36Sopenharmony_ci {0x23, 0x00001558}, {0x24, 0x00000060}, 7062306a36Sopenharmony_ci {0x25, 0x00000483}, {0x26, 0x0004f000}, 7162306a36Sopenharmony_ci {0x27, 0x000ec7d9}, {0x28, 0x00057730}, 7262306a36Sopenharmony_ci {0x29, 0x00004783}, {0x2a, 0x00000001}, 7362306a36Sopenharmony_ci {0x2b, 0x00021334}, {0x2a, 0x00000000}, 7462306a36Sopenharmony_ci {0x2b, 0x00000054}, {0x2a, 0x00000001}, 7562306a36Sopenharmony_ci {0x2b, 0x00000808}, {0x2b, 0x00053333}, 7662306a36Sopenharmony_ci {0x2c, 0x0000000c}, {0x2a, 0x00000002}, 7762306a36Sopenharmony_ci {0x2b, 0x00000808}, {0x2b, 0x0005b333}, 7862306a36Sopenharmony_ci {0x2c, 0x0000000d}, {0x2a, 0x00000003}, 7962306a36Sopenharmony_ci {0x2b, 0x00000808}, {0x2b, 0x00063333}, 8062306a36Sopenharmony_ci {0x2c, 0x0000000d}, {0x2a, 0x00000004}, 8162306a36Sopenharmony_ci {0x2b, 0x00000808}, {0x2b, 0x0006b333}, 8262306a36Sopenharmony_ci {0x2c, 0x0000000d}, {0x2a, 0x00000005}, 8362306a36Sopenharmony_ci {0x2b, 0x00000808}, {0x2b, 0x00073333}, 8462306a36Sopenharmony_ci {0x2c, 0x0000000d}, {0x2a, 0x00000006}, 8562306a36Sopenharmony_ci {0x2b, 0x00000709}, {0x2b, 0x0005b333}, 8662306a36Sopenharmony_ci {0x2c, 0x0000000d}, {0x2a, 0x00000007}, 8762306a36Sopenharmony_ci {0x2b, 0x00000709}, {0x2b, 0x00063333}, 8862306a36Sopenharmony_ci {0x2c, 0x0000000d}, {0x2a, 0x00000008}, 8962306a36Sopenharmony_ci {0x2b, 0x0000060a}, {0x2b, 0x0004b333}, 9062306a36Sopenharmony_ci {0x2c, 0x0000000d}, {0x2a, 0x00000009}, 9162306a36Sopenharmony_ci {0x2b, 0x0000060a}, {0x2b, 0x00053333}, 9262306a36Sopenharmony_ci {0x2c, 0x0000000d}, {0x2a, 0x0000000a}, 9362306a36Sopenharmony_ci {0x2b, 0x0000060a}, {0x2b, 0x0005b333}, 9462306a36Sopenharmony_ci {0x2c, 0x0000000d}, {0x2a, 0x0000000b}, 9562306a36Sopenharmony_ci {0x2b, 0x0000060a}, {0x2b, 0x00063333}, 9662306a36Sopenharmony_ci {0x2c, 0x0000000d}, {0x2a, 0x0000000c}, 9762306a36Sopenharmony_ci {0x2b, 0x0000060a}, {0x2b, 0x0006b333}, 9862306a36Sopenharmony_ci {0x2c, 0x0000000d}, {0x2a, 0x0000000d}, 9962306a36Sopenharmony_ci {0x2b, 0x0000060a}, {0x2b, 0x00073333}, 10062306a36Sopenharmony_ci {0x2c, 0x0000000d}, {0x2a, 0x0000000e}, 10162306a36Sopenharmony_ci {0x2b, 0x0000050b}, {0x2b, 0x00066666}, 10262306a36Sopenharmony_ci {0x2c, 0x0000001a}, {0x2a, 0x000e0000}, 10362306a36Sopenharmony_ci {0x10, 0x0004000f}, {0x11, 0x000e31fc}, 10462306a36Sopenharmony_ci {0x10, 0x0006000f}, {0x11, 0x000ff9f8}, 10562306a36Sopenharmony_ci {0x10, 0x0002000f}, {0x11, 0x000203f9}, 10662306a36Sopenharmony_ci {0x10, 0x0003000f}, {0x11, 0x000ff500}, 10762306a36Sopenharmony_ci {0x10, 0x00000000}, {0x11, 0x00000000}, 10862306a36Sopenharmony_ci {0x10, 0x0008000f}, {0x11, 0x0003f100}, 10962306a36Sopenharmony_ci {0x10, 0x0009000f}, {0x11, 0x00023100}, 11062306a36Sopenharmony_ci {0x12, 0x00032000}, {0x12, 0x00071000}, 11162306a36Sopenharmony_ci {0x12, 0x000b0000}, {0x12, 0x000fc000}, 11262306a36Sopenharmony_ci {0x13, 0x000287b3}, {0x13, 0x000244b7}, 11362306a36Sopenharmony_ci {0x13, 0x000204ab}, {0x13, 0x0001c49f}, 11462306a36Sopenharmony_ci {0x13, 0x00018493}, {0x13, 0x0001429b}, 11562306a36Sopenharmony_ci {0x13, 0x00010299}, {0x13, 0x0000c29c}, 11662306a36Sopenharmony_ci {0x13, 0x000081a0}, {0x13, 0x000040ac}, 11762306a36Sopenharmony_ci {0x13, 0x00000020}, {0x14, 0x0001944c}, 11862306a36Sopenharmony_ci {0x14, 0x00059444}, {0x14, 0x0009944c}, 11962306a36Sopenharmony_ci {0x14, 0x000d9444}, {0x15, 0x0000f474}, 12062306a36Sopenharmony_ci {0x15, 0x0004f477}, {0x15, 0x0008f455}, 12162306a36Sopenharmony_ci {0x15, 0x000cf455}, {0x16, 0x00000339}, 12262306a36Sopenharmony_ci {0x16, 0x00040339}, {0x16, 0x00080339}, 12362306a36Sopenharmony_ci {0x16, 0x000c0366}, {0x00, 0x00010159}, 12462306a36Sopenharmony_ci {0x18, 0x0000f401}, {0xfe, 0x00000000}, 12562306a36Sopenharmony_ci {0xfe, 0x00000000}, {0x1f, 0x00000003}, 12662306a36Sopenharmony_ci {0xfe, 0x00000000}, {0xfe, 0x00000000}, 12762306a36Sopenharmony_ci {0x1e, 0x00000247}, {0x1f, 0x00000000}, 12862306a36Sopenharmony_ci {0x00, 0x00030159}, 12962306a36Sopenharmony_ci {0xff, 0xffffffff} 13062306a36Sopenharmony_ci}; 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_cistatic int rtl8723au_identify_chip(struct rtl8xxxu_priv *priv) 13362306a36Sopenharmony_ci{ 13462306a36Sopenharmony_ci struct device *dev = &priv->udev->dev; 13562306a36Sopenharmony_ci u32 val32, sys_cfg, vendor; 13662306a36Sopenharmony_ci int ret = 0; 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_ci sys_cfg = rtl8xxxu_read32(priv, REG_SYS_CFG); 13962306a36Sopenharmony_ci priv->chip_cut = u32_get_bits(sys_cfg, SYS_CFG_CHIP_VERSION_MASK); 14062306a36Sopenharmony_ci if (sys_cfg & SYS_CFG_TRP_VAUX_EN) { 14162306a36Sopenharmony_ci dev_info(dev, "Unsupported test chip\n"); 14262306a36Sopenharmony_ci ret = -ENOTSUPP; 14362306a36Sopenharmony_ci goto out; 14462306a36Sopenharmony_ci } 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_ci strscpy(priv->chip_name, "8723AU", sizeof(priv->chip_name)); 14762306a36Sopenharmony_ci priv->usb_interrupts = 1; 14862306a36Sopenharmony_ci priv->rtl_chip = RTL8723A; 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_ci priv->rf_paths = 1; 15162306a36Sopenharmony_ci priv->rx_paths = 1; 15262306a36Sopenharmony_ci priv->tx_paths = 1; 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_ci val32 = rtl8xxxu_read32(priv, REG_MULTI_FUNC_CTRL); 15562306a36Sopenharmony_ci if (val32 & MULTI_WIFI_FUNC_EN) 15662306a36Sopenharmony_ci priv->has_wifi = 1; 15762306a36Sopenharmony_ci if (val32 & MULTI_BT_FUNC_EN) 15862306a36Sopenharmony_ci priv->has_bluetooth = 1; 15962306a36Sopenharmony_ci if (val32 & MULTI_GPS_FUNC_EN) 16062306a36Sopenharmony_ci priv->has_gps = 1; 16162306a36Sopenharmony_ci priv->is_multi_func = 1; 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_ci vendor = sys_cfg & SYS_CFG_VENDOR_ID; 16462306a36Sopenharmony_ci rtl8xxxu_identify_vendor_1bit(priv, vendor); 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_ci val32 = rtl8xxxu_read32(priv, REG_GPIO_OUTSTS); 16762306a36Sopenharmony_ci priv->rom_rev = u32_get_bits(val32, GPIO_RF_RL_ID); 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_ci rtl8xxxu_config_endpoints_sie(priv); 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_ci /* 17262306a36Sopenharmony_ci * Fallback for devices that do not provide REG_NORMAL_SIE_EP_TX 17362306a36Sopenharmony_ci */ 17462306a36Sopenharmony_ci if (!priv->ep_tx_count) 17562306a36Sopenharmony_ci ret = rtl8xxxu_config_endpoints_no_sie(priv); 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_ciout: 17862306a36Sopenharmony_ci return ret; 17962306a36Sopenharmony_ci} 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_cistatic int rtl8723au_parse_efuse(struct rtl8xxxu_priv *priv) 18262306a36Sopenharmony_ci{ 18362306a36Sopenharmony_ci struct rtl8723au_efuse *efuse = &priv->efuse_wifi.efuse8723; 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_ci if (efuse->rtl_id != cpu_to_le16(0x8129)) 18662306a36Sopenharmony_ci return -EINVAL; 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_ci ether_addr_copy(priv->mac_addr, efuse->mac_addr); 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_ci memcpy(priv->cck_tx_power_index_A, 19162306a36Sopenharmony_ci efuse->cck_tx_power_index_A, 19262306a36Sopenharmony_ci sizeof(efuse->cck_tx_power_index_A)); 19362306a36Sopenharmony_ci memcpy(priv->cck_tx_power_index_B, 19462306a36Sopenharmony_ci efuse->cck_tx_power_index_B, 19562306a36Sopenharmony_ci sizeof(efuse->cck_tx_power_index_B)); 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_ci memcpy(priv->ht40_1s_tx_power_index_A, 19862306a36Sopenharmony_ci efuse->ht40_1s_tx_power_index_A, 19962306a36Sopenharmony_ci sizeof(efuse->ht40_1s_tx_power_index_A)); 20062306a36Sopenharmony_ci memcpy(priv->ht40_1s_tx_power_index_B, 20162306a36Sopenharmony_ci efuse->ht40_1s_tx_power_index_B, 20262306a36Sopenharmony_ci sizeof(efuse->ht40_1s_tx_power_index_B)); 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_ci memcpy(priv->ht20_tx_power_index_diff, 20562306a36Sopenharmony_ci efuse->ht20_tx_power_index_diff, 20662306a36Sopenharmony_ci sizeof(efuse->ht20_tx_power_index_diff)); 20762306a36Sopenharmony_ci memcpy(priv->ofdm_tx_power_index_diff, 20862306a36Sopenharmony_ci efuse->ofdm_tx_power_index_diff, 20962306a36Sopenharmony_ci sizeof(efuse->ofdm_tx_power_index_diff)); 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_ci memcpy(priv->ht40_max_power_offset, 21262306a36Sopenharmony_ci efuse->ht40_max_power_offset, 21362306a36Sopenharmony_ci sizeof(efuse->ht40_max_power_offset)); 21462306a36Sopenharmony_ci memcpy(priv->ht20_max_power_offset, 21562306a36Sopenharmony_ci efuse->ht20_max_power_offset, 21662306a36Sopenharmony_ci sizeof(efuse->ht20_max_power_offset)); 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_ci if (priv->efuse_wifi.efuse8723.version >= 0x01) 21962306a36Sopenharmony_ci priv->default_crystal_cap = priv->efuse_wifi.efuse8723.xtal_k & 0x3f; 22062306a36Sopenharmony_ci else 22162306a36Sopenharmony_ci priv->fops->set_crystal_cap = NULL; 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_ci priv->power_base = &rtl8723a_power_base; 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_ci return 0; 22662306a36Sopenharmony_ci} 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_cistatic int rtl8723au_load_firmware(struct rtl8xxxu_priv *priv) 22962306a36Sopenharmony_ci{ 23062306a36Sopenharmony_ci const char *fw_name; 23162306a36Sopenharmony_ci int ret; 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_ci switch (priv->chip_cut) { 23462306a36Sopenharmony_ci case 0: 23562306a36Sopenharmony_ci fw_name = "rtlwifi/rtl8723aufw_A.bin"; 23662306a36Sopenharmony_ci break; 23762306a36Sopenharmony_ci case 1: 23862306a36Sopenharmony_ci if (priv->enable_bluetooth) 23962306a36Sopenharmony_ci fw_name = "rtlwifi/rtl8723aufw_B.bin"; 24062306a36Sopenharmony_ci else 24162306a36Sopenharmony_ci fw_name = "rtlwifi/rtl8723aufw_B_NoBT.bin"; 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_ci break; 24462306a36Sopenharmony_ci default: 24562306a36Sopenharmony_ci return -EINVAL; 24662306a36Sopenharmony_ci } 24762306a36Sopenharmony_ci 24862306a36Sopenharmony_ci ret = rtl8xxxu_load_firmware(priv, fw_name); 24962306a36Sopenharmony_ci return ret; 25062306a36Sopenharmony_ci} 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_cistatic int rtl8723au_init_phy_rf(struct rtl8xxxu_priv *priv) 25362306a36Sopenharmony_ci{ 25462306a36Sopenharmony_ci int ret; 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_ci ret = rtl8xxxu_init_phy_rf(priv, rtl8723au_radioa_1t_init_table, RF_A); 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_ci /* Reduce 80M spur */ 25962306a36Sopenharmony_ci rtl8xxxu_write32(priv, REG_AFE_XTAL_CTRL, 0x0381808d); 26062306a36Sopenharmony_ci rtl8xxxu_write32(priv, REG_AFE_PLL_CTRL, 0xf0ffff83); 26162306a36Sopenharmony_ci rtl8xxxu_write32(priv, REG_AFE_PLL_CTRL, 0xf0ffff82); 26262306a36Sopenharmony_ci rtl8xxxu_write32(priv, REG_AFE_PLL_CTRL, 0xf0ffff83); 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_ci return ret; 26562306a36Sopenharmony_ci} 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_cistatic int rtl8723a_emu_to_active(struct rtl8xxxu_priv *priv) 26862306a36Sopenharmony_ci{ 26962306a36Sopenharmony_ci u8 val8; 27062306a36Sopenharmony_ci u32 val32; 27162306a36Sopenharmony_ci int count, ret = 0; 27262306a36Sopenharmony_ci 27362306a36Sopenharmony_ci /* 0x20[0] = 1 enable LDOA12 MACRO block for all interface*/ 27462306a36Sopenharmony_ci val8 = rtl8xxxu_read8(priv, REG_LDOA15_CTRL); 27562306a36Sopenharmony_ci val8 |= LDOA15_ENABLE; 27662306a36Sopenharmony_ci rtl8xxxu_write8(priv, REG_LDOA15_CTRL, val8); 27762306a36Sopenharmony_ci 27862306a36Sopenharmony_ci /* 0x67[0] = 0 to disable BT_GPS_SEL pins*/ 27962306a36Sopenharmony_ci val8 = rtl8xxxu_read8(priv, 0x0067); 28062306a36Sopenharmony_ci val8 &= ~BIT(4); 28162306a36Sopenharmony_ci rtl8xxxu_write8(priv, 0x0067, val8); 28262306a36Sopenharmony_ci 28362306a36Sopenharmony_ci mdelay(1); 28462306a36Sopenharmony_ci 28562306a36Sopenharmony_ci /* 0x00[5] = 0 release analog Ips to digital, 1:isolation */ 28662306a36Sopenharmony_ci val8 = rtl8xxxu_read8(priv, REG_SYS_ISO_CTRL); 28762306a36Sopenharmony_ci val8 &= ~SYS_ISO_ANALOG_IPS; 28862306a36Sopenharmony_ci rtl8xxxu_write8(priv, REG_SYS_ISO_CTRL, val8); 28962306a36Sopenharmony_ci 29062306a36Sopenharmony_ci /* disable SW LPS 0x04[10]= 0 */ 29162306a36Sopenharmony_ci val8 = rtl8xxxu_read8(priv, REG_APS_FSMCO + 1); 29262306a36Sopenharmony_ci val8 &= ~BIT(2); 29362306a36Sopenharmony_ci rtl8xxxu_write8(priv, REG_APS_FSMCO + 1, val8); 29462306a36Sopenharmony_ci 29562306a36Sopenharmony_ci /* wait till 0x04[17] = 1 power ready*/ 29662306a36Sopenharmony_ci for (count = RTL8XXXU_MAX_REG_POLL; count; count--) { 29762306a36Sopenharmony_ci val32 = rtl8xxxu_read32(priv, REG_APS_FSMCO); 29862306a36Sopenharmony_ci if (val32 & BIT(17)) 29962306a36Sopenharmony_ci break; 30062306a36Sopenharmony_ci 30162306a36Sopenharmony_ci udelay(10); 30262306a36Sopenharmony_ci } 30362306a36Sopenharmony_ci 30462306a36Sopenharmony_ci if (!count) { 30562306a36Sopenharmony_ci ret = -EBUSY; 30662306a36Sopenharmony_ci goto exit; 30762306a36Sopenharmony_ci } 30862306a36Sopenharmony_ci 30962306a36Sopenharmony_ci /* We should be able to optimize the following three entries into one */ 31062306a36Sopenharmony_ci 31162306a36Sopenharmony_ci /* release WLON reset 0x04[16]= 1*/ 31262306a36Sopenharmony_ci val8 = rtl8xxxu_read8(priv, REG_APS_FSMCO + 2); 31362306a36Sopenharmony_ci val8 |= BIT(0); 31462306a36Sopenharmony_ci rtl8xxxu_write8(priv, REG_APS_FSMCO + 2, val8); 31562306a36Sopenharmony_ci 31662306a36Sopenharmony_ci /* disable HWPDN 0x04[15]= 0*/ 31762306a36Sopenharmony_ci val8 = rtl8xxxu_read8(priv, REG_APS_FSMCO + 1); 31862306a36Sopenharmony_ci val8 &= ~BIT(7); 31962306a36Sopenharmony_ci rtl8xxxu_write8(priv, REG_APS_FSMCO + 1, val8); 32062306a36Sopenharmony_ci 32162306a36Sopenharmony_ci /* disable WL suspend*/ 32262306a36Sopenharmony_ci val8 = rtl8xxxu_read8(priv, REG_APS_FSMCO + 1); 32362306a36Sopenharmony_ci val8 &= ~(BIT(3) | BIT(4)); 32462306a36Sopenharmony_ci rtl8xxxu_write8(priv, REG_APS_FSMCO + 1, val8); 32562306a36Sopenharmony_ci 32662306a36Sopenharmony_ci /* set, then poll until 0 */ 32762306a36Sopenharmony_ci val32 = rtl8xxxu_read32(priv, REG_APS_FSMCO); 32862306a36Sopenharmony_ci val32 |= APS_FSMCO_MAC_ENABLE; 32962306a36Sopenharmony_ci rtl8xxxu_write32(priv, REG_APS_FSMCO, val32); 33062306a36Sopenharmony_ci 33162306a36Sopenharmony_ci for (count = RTL8XXXU_MAX_REG_POLL; count; count--) { 33262306a36Sopenharmony_ci val32 = rtl8xxxu_read32(priv, REG_APS_FSMCO); 33362306a36Sopenharmony_ci if ((val32 & APS_FSMCO_MAC_ENABLE) == 0) { 33462306a36Sopenharmony_ci ret = 0; 33562306a36Sopenharmony_ci break; 33662306a36Sopenharmony_ci } 33762306a36Sopenharmony_ci udelay(10); 33862306a36Sopenharmony_ci } 33962306a36Sopenharmony_ci 34062306a36Sopenharmony_ci if (!count) { 34162306a36Sopenharmony_ci ret = -EBUSY; 34262306a36Sopenharmony_ci goto exit; 34362306a36Sopenharmony_ci } 34462306a36Sopenharmony_ci 34562306a36Sopenharmony_ci /* 0x4C[23] = 0x4E[7] = 1, switch DPDT_SEL_P output from WL BB */ 34662306a36Sopenharmony_ci /* 34762306a36Sopenharmony_ci * Note: Vendor driver actually clears this bit, despite the 34862306a36Sopenharmony_ci * documentation claims it's being set! 34962306a36Sopenharmony_ci */ 35062306a36Sopenharmony_ci val8 = rtl8xxxu_read8(priv, REG_LEDCFG2); 35162306a36Sopenharmony_ci val8 |= LEDCFG2_DPDT_SELECT; 35262306a36Sopenharmony_ci val8 &= ~LEDCFG2_DPDT_SELECT; 35362306a36Sopenharmony_ci rtl8xxxu_write8(priv, REG_LEDCFG2, val8); 35462306a36Sopenharmony_ci 35562306a36Sopenharmony_ciexit: 35662306a36Sopenharmony_ci return ret; 35762306a36Sopenharmony_ci} 35862306a36Sopenharmony_ci 35962306a36Sopenharmony_cistatic int rtl8723au_power_on(struct rtl8xxxu_priv *priv) 36062306a36Sopenharmony_ci{ 36162306a36Sopenharmony_ci u8 val8; 36262306a36Sopenharmony_ci u16 val16; 36362306a36Sopenharmony_ci u32 val32; 36462306a36Sopenharmony_ci int ret; 36562306a36Sopenharmony_ci 36662306a36Sopenharmony_ci /* 36762306a36Sopenharmony_ci * RSV_CTRL 0x001C[7:0] = 0x00, unlock ISO/CLK/Power control register 36862306a36Sopenharmony_ci */ 36962306a36Sopenharmony_ci rtl8xxxu_write8(priv, REG_RSV_CTRL, 0x0); 37062306a36Sopenharmony_ci 37162306a36Sopenharmony_ci rtl8xxxu_disabled_to_emu(priv); 37262306a36Sopenharmony_ci 37362306a36Sopenharmony_ci ret = rtl8723a_emu_to_active(priv); 37462306a36Sopenharmony_ci if (ret) 37562306a36Sopenharmony_ci goto exit; 37662306a36Sopenharmony_ci 37762306a36Sopenharmony_ci /* 37862306a36Sopenharmony_ci * 0x0004[19] = 1, reset 8051 37962306a36Sopenharmony_ci */ 38062306a36Sopenharmony_ci val8 = rtl8xxxu_read8(priv, REG_APS_FSMCO + 2); 38162306a36Sopenharmony_ci val8 |= BIT(3); 38262306a36Sopenharmony_ci rtl8xxxu_write8(priv, REG_APS_FSMCO + 2, val8); 38362306a36Sopenharmony_ci 38462306a36Sopenharmony_ci /* 38562306a36Sopenharmony_ci * Enable MAC DMA/WMAC/SCHEDULE/SEC block 38662306a36Sopenharmony_ci * Set CR bit10 to enable 32k calibration. 38762306a36Sopenharmony_ci */ 38862306a36Sopenharmony_ci val16 = rtl8xxxu_read16(priv, REG_CR); 38962306a36Sopenharmony_ci val16 |= (CR_HCI_TXDMA_ENABLE | CR_HCI_RXDMA_ENABLE | 39062306a36Sopenharmony_ci CR_TXDMA_ENABLE | CR_RXDMA_ENABLE | 39162306a36Sopenharmony_ci CR_PROTOCOL_ENABLE | CR_SCHEDULE_ENABLE | 39262306a36Sopenharmony_ci CR_MAC_TX_ENABLE | CR_MAC_RX_ENABLE | 39362306a36Sopenharmony_ci CR_SECURITY_ENABLE | CR_CALTIMER_ENABLE); 39462306a36Sopenharmony_ci rtl8xxxu_write16(priv, REG_CR, val16); 39562306a36Sopenharmony_ci 39662306a36Sopenharmony_ci /* For EFuse PG */ 39762306a36Sopenharmony_ci val32 = rtl8xxxu_read32(priv, REG_EFUSE_CTRL); 39862306a36Sopenharmony_ci val32 &= ~(BIT(28) | BIT(29) | BIT(30)); 39962306a36Sopenharmony_ci val32 |= (0x06 << 28); 40062306a36Sopenharmony_ci rtl8xxxu_write32(priv, REG_EFUSE_CTRL, val32); 40162306a36Sopenharmony_ciexit: 40262306a36Sopenharmony_ci return ret; 40362306a36Sopenharmony_ci} 40462306a36Sopenharmony_ci 40562306a36Sopenharmony_ci#define XTAL1 GENMASK(23, 18) 40662306a36Sopenharmony_ci#define XTAL0 GENMASK(17, 12) 40762306a36Sopenharmony_ci 40862306a36Sopenharmony_civoid rtl8723a_set_crystal_cap(struct rtl8xxxu_priv *priv, u8 crystal_cap) 40962306a36Sopenharmony_ci{ 41062306a36Sopenharmony_ci struct rtl8xxxu_cfo_tracking *cfo = &priv->cfo_tracking; 41162306a36Sopenharmony_ci u32 val32; 41262306a36Sopenharmony_ci 41362306a36Sopenharmony_ci if (crystal_cap == cfo->crystal_cap) 41462306a36Sopenharmony_ci return; 41562306a36Sopenharmony_ci 41662306a36Sopenharmony_ci val32 = rtl8xxxu_read32(priv, REG_MAC_PHY_CTRL); 41762306a36Sopenharmony_ci 41862306a36Sopenharmony_ci dev_dbg(&priv->udev->dev, 41962306a36Sopenharmony_ci "%s: Adjusting crystal cap from 0x%x (actually 0x%lx 0x%lx) to 0x%x\n", 42062306a36Sopenharmony_ci __func__, 42162306a36Sopenharmony_ci cfo->crystal_cap, 42262306a36Sopenharmony_ci FIELD_GET(XTAL1, val32), 42362306a36Sopenharmony_ci FIELD_GET(XTAL0, val32), 42462306a36Sopenharmony_ci crystal_cap); 42562306a36Sopenharmony_ci 42662306a36Sopenharmony_ci val32 &= ~(XTAL1 | XTAL0); 42762306a36Sopenharmony_ci val32 |= FIELD_PREP(XTAL1, crystal_cap) | 42862306a36Sopenharmony_ci FIELD_PREP(XTAL0, crystal_cap); 42962306a36Sopenharmony_ci rtl8xxxu_write32(priv, REG_MAC_PHY_CTRL, val32); 43062306a36Sopenharmony_ci 43162306a36Sopenharmony_ci cfo->crystal_cap = crystal_cap; 43262306a36Sopenharmony_ci} 43362306a36Sopenharmony_ci 43462306a36Sopenharmony_cis8 rtl8723a_cck_rssi(struct rtl8xxxu_priv *priv, struct rtl8723au_phy_stats *phy_stats) 43562306a36Sopenharmony_ci{ 43662306a36Sopenharmony_ci u8 cck_agc_rpt = phy_stats->cck_agc_rpt_ofdm_cfosho_a; 43762306a36Sopenharmony_ci s8 rx_pwr_all = 0x00; 43862306a36Sopenharmony_ci 43962306a36Sopenharmony_ci switch (cck_agc_rpt & 0xc0) { 44062306a36Sopenharmony_ci case 0xc0: 44162306a36Sopenharmony_ci rx_pwr_all = -46 - (cck_agc_rpt & 0x3e); 44262306a36Sopenharmony_ci break; 44362306a36Sopenharmony_ci case 0x80: 44462306a36Sopenharmony_ci rx_pwr_all = -26 - (cck_agc_rpt & 0x3e); 44562306a36Sopenharmony_ci break; 44662306a36Sopenharmony_ci case 0x40: 44762306a36Sopenharmony_ci rx_pwr_all = -12 - (cck_agc_rpt & 0x3e); 44862306a36Sopenharmony_ci break; 44962306a36Sopenharmony_ci case 0x00: 45062306a36Sopenharmony_ci rx_pwr_all = 16 - (cck_agc_rpt & 0x3e); 45162306a36Sopenharmony_ci break; 45262306a36Sopenharmony_ci } 45362306a36Sopenharmony_ci 45462306a36Sopenharmony_ci return rx_pwr_all; 45562306a36Sopenharmony_ci} 45662306a36Sopenharmony_ci 45762306a36Sopenharmony_cistatic int rtl8723au_led_brightness_set(struct led_classdev *led_cdev, 45862306a36Sopenharmony_ci enum led_brightness brightness) 45962306a36Sopenharmony_ci{ 46062306a36Sopenharmony_ci struct rtl8xxxu_priv *priv = container_of(led_cdev, 46162306a36Sopenharmony_ci struct rtl8xxxu_priv, 46262306a36Sopenharmony_ci led_cdev); 46362306a36Sopenharmony_ci u8 ledcfg = rtl8xxxu_read8(priv, REG_LEDCFG2); 46462306a36Sopenharmony_ci 46562306a36Sopenharmony_ci if (brightness == LED_OFF) { 46662306a36Sopenharmony_ci ledcfg &= ~LEDCFG2_HW_LED_CONTROL; 46762306a36Sopenharmony_ci ledcfg |= LEDCFG2_SW_LED_CONTROL | LEDCFG2_SW_LED_DISABLE; 46862306a36Sopenharmony_ci } else if (brightness == LED_ON) { 46962306a36Sopenharmony_ci ledcfg &= ~(LEDCFG2_HW_LED_CONTROL | LEDCFG2_SW_LED_DISABLE); 47062306a36Sopenharmony_ci ledcfg |= LEDCFG2_SW_LED_CONTROL; 47162306a36Sopenharmony_ci } else if (brightness == RTL8XXXU_HW_LED_CONTROL) { 47262306a36Sopenharmony_ci ledcfg &= ~LEDCFG2_SW_LED_DISABLE; 47362306a36Sopenharmony_ci ledcfg |= LEDCFG2_HW_LED_CONTROL | LEDCFG2_HW_LED_ENABLE; 47462306a36Sopenharmony_ci } 47562306a36Sopenharmony_ci 47662306a36Sopenharmony_ci rtl8xxxu_write8(priv, REG_LEDCFG2, ledcfg); 47762306a36Sopenharmony_ci 47862306a36Sopenharmony_ci return 0; 47962306a36Sopenharmony_ci} 48062306a36Sopenharmony_ci 48162306a36Sopenharmony_cistruct rtl8xxxu_fileops rtl8723au_fops = { 48262306a36Sopenharmony_ci .identify_chip = rtl8723au_identify_chip, 48362306a36Sopenharmony_ci .parse_efuse = rtl8723au_parse_efuse, 48462306a36Sopenharmony_ci .load_firmware = rtl8723au_load_firmware, 48562306a36Sopenharmony_ci .power_on = rtl8723au_power_on, 48662306a36Sopenharmony_ci .power_off = rtl8xxxu_power_off, 48762306a36Sopenharmony_ci .read_efuse = rtl8xxxu_read_efuse, 48862306a36Sopenharmony_ci .reset_8051 = rtl8xxxu_reset_8051, 48962306a36Sopenharmony_ci .llt_init = rtl8xxxu_init_llt_table, 49062306a36Sopenharmony_ci .init_phy_bb = rtl8xxxu_gen1_init_phy_bb, 49162306a36Sopenharmony_ci .init_phy_rf = rtl8723au_init_phy_rf, 49262306a36Sopenharmony_ci .phy_lc_calibrate = rtl8723a_phy_lc_calibrate, 49362306a36Sopenharmony_ci .phy_iq_calibrate = rtl8xxxu_gen1_phy_iq_calibrate, 49462306a36Sopenharmony_ci .config_channel = rtl8xxxu_gen1_config_channel, 49562306a36Sopenharmony_ci .parse_rx_desc = rtl8xxxu_parse_rxdesc16, 49662306a36Sopenharmony_ci .parse_phystats = rtl8723au_rx_parse_phystats, 49762306a36Sopenharmony_ci .init_aggregation = rtl8xxxu_gen1_init_aggregation, 49862306a36Sopenharmony_ci .enable_rf = rtl8xxxu_gen1_enable_rf, 49962306a36Sopenharmony_ci .disable_rf = rtl8xxxu_gen1_disable_rf, 50062306a36Sopenharmony_ci .usb_quirks = rtl8xxxu_gen1_usb_quirks, 50162306a36Sopenharmony_ci .set_tx_power = rtl8xxxu_gen1_set_tx_power, 50262306a36Sopenharmony_ci .update_rate_mask = rtl8xxxu_update_rate_mask, 50362306a36Sopenharmony_ci .report_connect = rtl8xxxu_gen1_report_connect, 50462306a36Sopenharmony_ci .report_rssi = rtl8xxxu_gen1_report_rssi, 50562306a36Sopenharmony_ci .fill_txdesc = rtl8xxxu_fill_txdesc_v1, 50662306a36Sopenharmony_ci .set_crystal_cap = rtl8723a_set_crystal_cap, 50762306a36Sopenharmony_ci .cck_rssi = rtl8723a_cck_rssi, 50862306a36Sopenharmony_ci .led_classdev_brightness_set = rtl8723au_led_brightness_set, 50962306a36Sopenharmony_ci .writeN_block_size = 1024, 51062306a36Sopenharmony_ci .rx_agg_buf_size = 16000, 51162306a36Sopenharmony_ci .tx_desc_size = sizeof(struct rtl8xxxu_txdesc32), 51262306a36Sopenharmony_ci .rx_desc_size = sizeof(struct rtl8xxxu_rxdesc16), 51362306a36Sopenharmony_ci .adda_1t_init = 0x0b1b25a0, 51462306a36Sopenharmony_ci .adda_1t_path_on = 0x0bdb25a0, 51562306a36Sopenharmony_ci .adda_2t_path_on_a = 0x04db25a4, 51662306a36Sopenharmony_ci .adda_2t_path_on_b = 0x0b1b25a4, 51762306a36Sopenharmony_ci .trxff_boundary = 0x27ff, 51862306a36Sopenharmony_ci .pbp_rx = PBP_PAGE_SIZE_128, 51962306a36Sopenharmony_ci .pbp_tx = PBP_PAGE_SIZE_128, 52062306a36Sopenharmony_ci .mactable = rtl8xxxu_gen1_mac_init_table, 52162306a36Sopenharmony_ci .total_page_num = TX_TOTAL_PAGE_NUM, 52262306a36Sopenharmony_ci .page_num_hi = TX_PAGE_NUM_HI_PQ, 52362306a36Sopenharmony_ci .page_num_lo = TX_PAGE_NUM_LO_PQ, 52462306a36Sopenharmony_ci .page_num_norm = TX_PAGE_NUM_NORM_PQ, 52562306a36Sopenharmony_ci}; 526