Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 


Quelle  core.c   Sprache: C

 
// SPDX-License-Identifier: GPL-2.0-only
/*
 * RTL8XXXU mac80211 USB driver
 *
 * Copyright (c) 2014 - 2017 Jes Sorensen <Jes.Sorensen@gmail.com>
 *
 * Portions, notably calibration code:
 * Copyright(c) 2007 - 2011 Realtek Corporation. All rights reserved.
 *
 * This driver was written as a replacement for the vendor provided
 * rtl8723au driver. As the Realtek 8xxx chips are very similar in
 * their programming interface, I have started adding support for
 * additional 8xxx chips like the 8192cu, 8188cus, etc.
 */


#include <linux/firmware.h>
#include "regs.h"
#include "rtl8xxxu.h"

#define DRIVER_NAME "rtl8xxxu"

int rtl8xxxu_debug;
static bool rtl8xxxu_ht40_2g;
static bool rtl8xxxu_dma_aggregation;
static int rtl8xxxu_dma_agg_timeout = -1;
static int rtl8xxxu_dma_agg_pages = -1;

MODULE_AUTHOR("Jes Sorensen ");
MODULE_DESCRIPTION("RTL8XXXu USB mac80211 Wireless LAN Driver");
MODULE_LICENSE("GPL");
MODULE_FIRMWARE("rtlwifi/rtl8723aufw_A.bin");
MODULE_FIRMWARE("rtlwifi/rtl8723aufw_B.bin");
MODULE_FIRMWARE("rtlwifi/rtl8723aufw_B_NoBT.bin");
MODULE_FIRMWARE("rtlwifi/rtl8188eufw.bin");
MODULE_FIRMWARE("rtlwifi/rtl8192cufw_A.bin");
MODULE_FIRMWARE("rtlwifi/rtl8192cufw_B.bin");
MODULE_FIRMWARE("rtlwifi/rtl8192cufw_TMSC.bin");
MODULE_FIRMWARE("rtlwifi/rtl8192eu_nic.bin");
MODULE_FIRMWARE("rtlwifi/rtl8723bu_nic.bin");
MODULE_FIRMWARE("rtlwifi/rtl8723bu_bt.bin");
MODULE_FIRMWARE("rtlwifi/rtl8188fufw.bin");
MODULE_FIRMWARE("rtlwifi/rtl8710bufw_SMIC.bin");
MODULE_FIRMWARE("rtlwifi/rtl8710bufw_UMC.bin");
MODULE_FIRMWARE("rtlwifi/rtl8192fufw.bin");

module_param_named(debug, rtl8xxxu_debug, int, 0600);
MODULE_PARM_DESC(debug, "Set debug mask");
module_param_named(ht40_2g, rtl8xxxu_ht40_2g, bool, 0600);
MODULE_PARM_DESC(ht40_2g, "Enable HT40 support on the 2.4GHz band");
module_param_named(dma_aggregation, rtl8xxxu_dma_aggregation, bool, 0600);
MODULE_PARM_DESC(dma_aggregation, "Enable DMA packet aggregation");
module_param_named(dma_agg_timeout, rtl8xxxu_dma_agg_timeout, int, 0600);
MODULE_PARM_DESC(dma_agg_timeout, "Set DMA aggregation timeout (range 1-127)");
module_param_named(dma_agg_pages, rtl8xxxu_dma_agg_pages, int, 0600);
MODULE_PARM_DESC(dma_agg_pages, "Set DMA aggregation pages (range 1-127, 0 to disable)");

#define USB_VENDOR_ID_REALTEK  0x0bda
#define RTL8XXXU_RX_URBS  32
#define RTL8XXXU_RX_URB_PENDING_WATER 8
#define RTL8XXXU_TX_URBS  64
#define RTL8XXXU_TX_URB_LOW_WATER 25
#define RTL8XXXU_TX_URB_HIGH_WATER 32

static int rtl8xxxu_submit_rx_urb(struct rtl8xxxu_priv *priv,
      struct rtl8xxxu_rx_urb *rx_urb);

static struct ieee80211_rate rtl8xxxu_rates[] = {
 { .bitrate = 10, .hw_value = DESC_RATE_1M, .flags = 0 },
 { .bitrate = 20, .hw_value = DESC_RATE_2M, .flags = 0 },
 { .bitrate = 55, .hw_value = DESC_RATE_5_5M, .flags = 0 },
 { .bitrate = 110, .hw_value = DESC_RATE_11M, .flags = 0 },
 { .bitrate = 60, .hw_value = DESC_RATE_6M, .flags = 0 },
 { .bitrate = 90, .hw_value = DESC_RATE_9M, .flags = 0 },
 { .bitrate = 120, .hw_value = DESC_RATE_12M, .flags = 0 },
 { .bitrate = 180, .hw_value = DESC_RATE_18M, .flags = 0 },
 { .bitrate = 240, .hw_value = DESC_RATE_24M, .flags = 0 },
 { .bitrate = 360, .hw_value = DESC_RATE_36M, .flags = 0 },
 { .bitrate = 480, .hw_value = DESC_RATE_48M, .flags = 0 },
 { .bitrate = 540, .hw_value = DESC_RATE_54M, .flags = 0 },
};

static struct ieee80211_channel rtl8xxxu_channels_2g[] = {
 { .band = NL80211_BAND_2GHZ, .center_freq = 2412,
   .hw_value = 1, .max_power = 30 },
 { .band = NL80211_BAND_2GHZ, .center_freq = 2417,
   .hw_value = 2, .max_power = 30 },
 { .band = NL80211_BAND_2GHZ, .center_freq = 2422,
   .hw_value = 3, .max_power = 30 },
 { .band = NL80211_BAND_2GHZ, .center_freq = 2427,
   .hw_value = 4, .max_power = 30 },
 { .band = NL80211_BAND_2GHZ, .center_freq = 2432,
   .hw_value = 5, .max_power = 30 },
 { .band = NL80211_BAND_2GHZ, .center_freq = 2437,
   .hw_value = 6, .max_power = 30 },
 { .band = NL80211_BAND_2GHZ, .center_freq = 2442,
   .hw_value = 7, .max_power = 30 },
 { .band = NL80211_BAND_2GHZ, .center_freq = 2447,
   .hw_value = 8, .max_power = 30 },
 { .band = NL80211_BAND_2GHZ, .center_freq = 2452,
   .hw_value = 9, .max_power = 30 },
 { .band = NL80211_BAND_2GHZ, .center_freq = 2457,
   .hw_value = 10, .max_power = 30 },
 { .band = NL80211_BAND_2GHZ, .center_freq = 2462,
   .hw_value = 11, .max_power = 30 },
 { .band = NL80211_BAND_2GHZ, .center_freq = 2467,
   .hw_value = 12, .max_power = 30 },
 { .band = NL80211_BAND_2GHZ, .center_freq = 2472,
   .hw_value = 13, .max_power = 30 },
 { .band = NL80211_BAND_2GHZ, .center_freq = 2484,
   .hw_value = 14, .max_power = 30 }
};

static struct ieee80211_supported_band rtl8xxxu_supported_band = {
 .channels = rtl8xxxu_channels_2g,
 .n_channels = ARRAY_SIZE(rtl8xxxu_channels_2g),
 .bitrates = rtl8xxxu_rates,
 .n_bitrates = ARRAY_SIZE(rtl8xxxu_rates),
};

static const struct rtl8xxxu_reg32val rtl8723a_phy_1t_init_table[] = {
 {0x800, 0x80040000}, {0x804, 0x00000003},
 {0x808, 0x0000fc00}, {0x80c, 0x0000000a},
 {0x810, 0x10001331}, {0x814, 0x020c3d10},
 {0x818, 0x02200385}, {0x81c, 0x00000000},
 {0x820, 0x01000100}, {0x824, 0x00390004},
 {0x828, 0x00000000}, {0x82c, 0x00000000},
 {0x830, 0x00000000}, {0x834, 0x00000000},
 {0x838, 0x00000000}, {0x83c, 0x00000000},
 {0x840, 0x00010000}, {0x844, 0x00000000},
 {0x848, 0x00000000}, {0x84c, 0x00000000},
 {0x850, 0x00000000}, {0x854, 0x00000000},
 {0x858, 0x569a569a}, {0x85c, 0x001b25a4},
 {0x860, 0x66f60110}, {0x864, 0x061f0130},
 {0x868, 0x00000000}, {0x86c, 0x32323200},
 {0x870, 0x07000760}, {0x874, 0x22004000},
 {0x878, 0x00000808}, {0x87c, 0x00000000},
 {0x880, 0xc0083070}, {0x884, 0x000004d5},
 {0x888, 0x00000000}, {0x88c, 0xccc000c0},
 {0x890, 0x00000800}, {0x894, 0xfffffffe},
 {0x898, 0x40302010}, {0x89c, 0x00706050},
 {0x900, 0x00000000}, {0x904, 0x00000023},
 {0x908, 0x00000000}, {0x90c, 0x81121111},
 {0xa00, 0x00d047c8}, {0xa04, 0x80ff000c},
 {0xa08, 0x8c838300}, {0xa0c, 0x2e68120f},
 {0xa10, 0x9500bb78}, {0xa14, 0x11144028},
 {0xa18, 0x00881117}, {0xa1c, 0x89140f00},
 {0xa20, 0x1a1b0000}, {0xa24, 0x090e1317},
 {0xa28, 0x00000204}, {0xa2c, 0x00d30000},
 {0xa70, 0x101fbf00}, {0xa74, 0x00000007},
 {0xa78, 0x00000900},
 {0xc00, 0x48071d40}, {0xc04, 0x03a05611},
 {0xc08, 0x000000e4}, {0xc0c, 0x6c6c6c6c},
 {0xc10, 0x08800000}, {0xc14, 0x40000100},
 {0xc18, 0x08800000}, {0xc1c, 0x40000100},
 {0xc20, 0x00000000}, {0xc24, 0x00000000},
 {0xc28, 0x00000000}, {0xc2c, 0x00000000},
 {0xc30, 0x69e9ac44}, {0xc34, 0x469652af},
 {0xc38, 0x49795994}, {0xc3c, 0x0a97971c},
 {0xc40, 0x1f7c403f}, {0xc44, 0x000100b7},
 {0xc48, 0xec020107}, {0xc4c, 0x007f037f},
 {0xc50, 0x69543420}, {0xc54, 0x43bc0094},
 {0xc58, 0x69543420}, {0xc5c, 0x433c0094},
 {0xc60, 0x00000000}, {0xc64, 0x7112848b},
 {0xc68, 0x47c00bff}, {0xc6c, 0x00000036},
 {0xc70, 0x2c7f000d}, {0xc74, 0x018610db},
 {0xc78, 0x0000001f}, {0xc7c, 0x00b91612},
 {0xc80, 0x40000100}, {0xc84, 0x20f60000},
 {0xc88, 0x40000100}, {0xc8c, 0x20200000},
 {0xc90, 0x00121820}, {0xc94, 0x00000000},
 {0xc98, 0x00121820}, {0xc9c, 0x00007f7f},
 {0xca0, 0x00000000}, {0xca4, 0x00000080},
 {0xca8, 0x00000000}, {0xcac, 0x00000000},
 {0xcb0, 0x00000000}, {0xcb4, 0x00000000},
 {0xcb8, 0x00000000}, {0xcbc, 0x28000000},
 {0xcc0, 0x00000000}, {0xcc4, 0x00000000},
 {0xcc8, 0x00000000}, {0xccc, 0x00000000},
 {0xcd0, 0x00000000}, {0xcd4, 0x00000000},
 {0xcd8, 0x64b22427}, {0xcdc, 0x00766932},
 {0xce0, 0x00222222}, {0xce4, 0x00000000},
 {0xce8, 0x37644302}, {0xcec, 0x2f97d40c},
 {0xd00, 0x00080740}, {0xd04, 0x00020401},
 {0xd08, 0x0000907f}, {0xd0c, 0x20010201},
 {0xd10, 0xa0633333}, {0xd14, 0x3333bc43},
 {0xd18, 0x7a8f5b6b}, {0xd2c, 0xcc979975},
 {0xd30, 0x00000000}, {0xd34, 0x80608000},
 {0xd38, 0x00000000}, {0xd3c, 0x00027293},
 {0xd40, 0x00000000}, {0xd44, 0x00000000},
 {0xd48, 0x00000000}, {0xd4c, 0x00000000},
 {0xd50, 0x6437140a}, {0xd54, 0x00000000},
 {0xd58, 0x00000000}, {0xd5c, 0x30032064},
 {0xd60, 0x4653de68}, {0xd64, 0x04518a3c},
 {0xd68, 0x00002101}, {0xd6c, 0x2a201c16},
 {0xd70, 0x1812362e}, {0xd74, 0x322c2220},
 {0xd78, 0x000e3c24}, {0xe00, 0x2a2a2a2a},
 {0xe04, 0x2a2a2a2a}, {0xe08, 0x03902a2a},
 {0xe10, 0x2a2a2a2a}, {0xe14, 0x2a2a2a2a},
 {0xe18, 0x2a2a2a2a}, {0xe1c, 0x2a2a2a2a},
 {0xe28, 0x00000000}, {0xe30, 0x1000dc1f},
 {0xe34, 0x10008c1f}, {0xe38, 0x02140102},
 {0xe3c, 0x681604c2}, {0xe40, 0x01007c00},
 {0xe44, 0x01004800}, {0xe48, 0xfb000000},
 {0xe4c, 0x000028d1}, {0xe50, 0x1000dc1f},
 {0xe54, 0x10008c1f}, {0xe58, 0x02140102},
 {0xe5c, 0x28160d05}, {0xe60, 0x00000008},
 {0xe68, 0x001b25a4}, {0xe6c, 0x631b25a0},
 {0xe70, 0x631b25a0}, {0xe74, 0x081b25a0},
 {0xe78, 0x081b25a0}, {0xe7c, 0x081b25a0},
 {0xe80, 0x081b25a0}, {0xe84, 0x631b25a0},
 {0xe88, 0x081b25a0}, {0xe8c, 0x631b25a0},
 {0xed0, 0x631b25a0}, {0xed4, 0x631b25a0},
 {0xed8, 0x631b25a0}, {0xedc, 0x001b25a0},
 {0xee0, 0x001b25a0}, {0xeec, 0x6b1b25a0},
 {0xf14, 0x00000003}, {0xf4c, 0x00000000},
 {0xf00, 0x00000300},
 {0xffff, 0xffffffff},
};

static const struct rtl8xxxu_reg32val rtl8192cu_phy_2t_init_table[] = {
 {0x024, 0x0011800f}, {0x028, 0x00ffdb83},
 {0x800, 0x80040002}, {0x804, 0x00000003},
 {0x808, 0x0000fc00}, {0x80c, 0x0000000a},
 {0x810, 0x10000330}, {0x814, 0x020c3d10},
 {0x818, 0x02200385}, {0x81c, 0x00000000},
 {0x820, 0x01000100}, {0x824, 0x00390004},
 {0x828, 0x01000100}, {0x82c, 0x00390004},
 {0x830, 0x27272727}, {0x834, 0x27272727},
 {0x838, 0x27272727}, {0x83c, 0x27272727},
 {0x840, 0x00010000}, {0x844, 0x00010000},
 {0x848, 0x27272727}, {0x84c, 0x27272727},
 {0x850, 0x00000000}, {0x854, 0x00000000},
 {0x858, 0x569a569a}, {0x85c, 0x0c1b25a4},
 {0x860, 0x66e60230}, {0x864, 0x061f0130},
 {0x868, 0x27272727}, {0x86c, 0x2b2b2b27},
 {0x870, 0x07000700}, {0x874, 0x22184000},
 {0x878, 0x08080808}, {0x87c, 0x00000000},
 {0x880, 0xc0083070}, {0x884, 0x000004d5},
 {0x888, 0x00000000}, {0x88c, 0xcc0000c0},
 {0x890, 0x00000800}, {0x894, 0xfffffffe},
 {0x898, 0x40302010}, {0x89c, 0x00706050},
 {0x900, 0x00000000}, {0x904, 0x00000023},
 {0x908, 0x00000000}, {0x90c, 0x81121313},
 {0xa00, 0x00d047c8}, {0xa04, 0x80ff000c},
 {0xa08, 0x8c838300}, {0xa0c, 0x2e68120f},
 {0xa10, 0x9500bb78}, {0xa14, 0x11144028},
 {0xa18, 0x00881117}, {0xa1c, 0x89140f00},
 {0xa20, 0x1a1b0000}, {0xa24, 0x090e1317},
 {0xa28, 0x00000204}, {0xa2c, 0x00d30000},
 {0xa70, 0x101fbf00}, {0xa74, 0x00000007},
 {0xc00, 0x48071d40}, {0xc04, 0x03a05633},
 {0xc08, 0x000000e4}, {0xc0c, 0x6c6c6c6c},
 {0xc10, 0x08800000}, {0xc14, 0x40000100},
 {0xc18, 0x08800000}, {0xc1c, 0x40000100},
 {0xc20, 0x00000000}, {0xc24, 0x00000000},
 {0xc28, 0x00000000}, {0xc2c, 0x00000000},
 {0xc30, 0x69e9ac44}, {0xc34, 0x469652cf},
 {0xc38, 0x49795994}, {0xc3c, 0x0a97971c},
 {0xc40, 0x1f7c403f}, {0xc44, 0x000100b7},
 {0xc48, 0xec020107}, {0xc4c, 0x007f037f},
 {0xc50, 0x69543420}, {0xc54, 0x43bc0094},
 {0xc58, 0x69543420}, {0xc5c, 0x433c0094},
 {0xc60, 0x00000000}, {0xc64, 0x5116848b},
 {0xc68, 0x47c00bff}, {0xc6c, 0x00000036},
 {0xc70, 0x2c7f000d}, {0xc74, 0x2186115b},
 {0xc78, 0x0000001f}, {0xc7c, 0x00b99612},
 {0xc80, 0x40000100}, {0xc84, 0x20f60000},
 {0xc88, 0x40000100}, {0xc8c, 0xa0e40000},
 {0xc90, 0x00121820}, {0xc94, 0x00000000},
 {0xc98, 0x00121820}, {0xc9c, 0x00007f7f},
 {0xca0, 0x00000000}, {0xca4, 0x00000080},
 {0xca8, 0x00000000}, {0xcac, 0x00000000},
 {0xcb0, 0x00000000}, {0xcb4, 0x00000000},
 {0xcb8, 0x00000000}, {0xcbc, 0x28000000},
 {0xcc0, 0x00000000}, {0xcc4, 0x00000000},
 {0xcc8, 0x00000000}, {0xccc, 0x00000000},
 {0xcd0, 0x00000000}, {0xcd4, 0x00000000},
 {0xcd8, 0x64b22427}, {0xcdc, 0x00766932},
 {0xce0, 0x00222222}, {0xce4, 0x00000000},
 {0xce8, 0x37644302}, {0xcec, 0x2f97d40c},
 {0xd00, 0x00080740}, {0xd04, 0x00020403},
 {0xd08, 0x0000907f}, {0xd0c, 0x20010201},
 {0xd10, 0xa0633333}, {0xd14, 0x3333bc43},
 {0xd18, 0x7a8f5b6b}, {0xd2c, 0xcc979975},
 {0xd30, 0x00000000}, {0xd34, 0x80608000},
 {0xd38, 0x00000000}, {0xd3c, 0x00027293},
 {0xd40, 0x00000000}, {0xd44, 0x00000000},
 {0xd48, 0x00000000}, {0xd4c, 0x00000000},
 {0xd50, 0x6437140a}, {0xd54, 0x00000000},
 {0xd58, 0x00000000}, {0xd5c, 0x30032064},
 {0xd60, 0x4653de68}, {0xd64, 0x04518a3c},
 {0xd68, 0x00002101}, {0xd6c, 0x2a201c16},
 {0xd70, 0x1812362e}, {0xd74, 0x322c2220},
 {0xd78, 0x000e3c24}, {0xe00, 0x2a2a2a2a},
 {0xe04, 0x2a2a2a2a}, {0xe08, 0x03902a2a},
 {0xe10, 0x2a2a2a2a}, {0xe14, 0x2a2a2a2a},
 {0xe18, 0x2a2a2a2a}, {0xe1c, 0x2a2a2a2a},
 {0xe28, 0x00000000}, {0xe30, 0x1000dc1f},
 {0xe34, 0x10008c1f}, {0xe38, 0x02140102},
 {0xe3c, 0x681604c2}, {0xe40, 0x01007c00},
 {0xe44, 0x01004800}, {0xe48, 0xfb000000},
 {0xe4c, 0x000028d1}, {0xe50, 0x1000dc1f},
 {0xe54, 0x10008c1f}, {0xe58, 0x02140102},
 {0xe5c, 0x28160d05}, {0xe60, 0x00000010},
 {0xe68, 0x001b25a4}, {0xe6c, 0x63db25a4},
 {0xe70, 0x63db25a4}, {0xe74, 0x0c1b25a4},
 {0xe78, 0x0c1b25a4}, {0xe7c, 0x0c1b25a4},
 {0xe80, 0x0c1b25a4}, {0xe84, 0x63db25a4},
 {0xe88, 0x0c1b25a4}, {0xe8c, 0x63db25a4},
 {0xed0, 0x63db25a4}, {0xed4, 0x63db25a4},
 {0xed8, 0x63db25a4}, {0xedc, 0x001b25a4},
 {0xee0, 0x001b25a4}, {0xeec, 0x6fdb25a4},
 {0xf14, 0x00000003}, {0xf4c, 0x00000000},
 {0xf00, 0x00000300},
 {0xffff, 0xffffffff},
};

static const struct rtl8xxxu_reg32val rtl8188ru_phy_1t_highpa_table[] = {
 {0x024, 0x0011800f}, {0x028, 0x00ffdb83},
 {0x040, 0x000c0004}, {0x800, 0x80040000},
 {0x804, 0x00000001}, {0x808, 0x0000fc00},
 {0x80c, 0x0000000a}, {0x810, 0x10005388},
 {0x814, 0x020c3d10}, {0x818, 0x02200385},
 {0x81c, 0x00000000}, {0x820, 0x01000100},
 {0x824, 0x00390204}, {0x828, 0x00000000},
 {0x82c, 0x00000000}, {0x830, 0x00000000},
 {0x834, 0x00000000}, {0x838, 0x00000000},
 {0x83c, 0x00000000}, {0x840, 0x00010000},
 {0x844, 0x00000000}, {0x848, 0x00000000},
 {0x84c, 0x00000000}, {0x850, 0x00000000},
 {0x854, 0x00000000}, {0x858, 0x569a569a},
 {0x85c, 0x001b25a4}, {0x860, 0x66e60230},
 {0x864, 0x061f0130}, {0x868, 0x00000000},
 {0x86c, 0x20202000}, {0x870, 0x03000300},
 {0x874, 0x22004000}, {0x878, 0x00000808},
 {0x87c, 0x00ffc3f1}, {0x880, 0xc0083070},
 {0x884, 0x000004d5}, {0x888, 0x00000000},
 {0x88c, 0xccc000c0}, {0x890, 0x00000800},
 {0x894, 0xfffffffe}, {0x898, 0x40302010},
 {0x89c, 0x00706050}, {0x900, 0x00000000},
 {0x904, 0x00000023}, {0x908, 0x00000000},
 {0x90c, 0x81121111}, {0xa00, 0x00d047c8},
 {0xa04, 0x80ff000c}, {0xa08, 0x8c838300},
 {0xa0c, 0x2e68120f}, {0xa10, 0x9500bb78},
 {0xa14, 0x11144028}, {0xa18, 0x00881117},
 {0xa1c, 0x89140f00}, {0xa20, 0x15160000},
 {0xa24, 0x070b0f12}, {0xa28, 0x00000104},
 {0xa2c, 0x00d30000}, {0xa70, 0x101fbf00},
 {0xa74, 0x00000007}, {0xc00, 0x48071d40},
 {0xc04, 0x03a05611}, {0xc08, 0x000000e4},
 {0xc0c, 0x6c6c6c6c}, {0xc10, 0x08800000},
 {0xc14, 0x40000100}, {0xc18, 0x08800000},
 {0xc1c, 0x40000100}, {0xc20, 0x00000000},
 {0xc24, 0x00000000}, {0xc28, 0x00000000},
 {0xc2c, 0x00000000}, {0xc30, 0x69e9ac44},
 {0xc34, 0x469652cf}, {0xc38, 0x49795994},
 {0xc3c, 0x0a97971c}, {0xc40, 0x1f7c403f},
 {0xc44, 0x000100b7}, {0xc48, 0xec020107},
 {0xc4c, 0x007f037f}, {0xc50, 0x6954342e},
 {0xc54, 0x43bc0094}, {0xc58, 0x6954342f},
 {0xc5c, 0x433c0094}, {0xc60, 0x00000000},
 {0xc64, 0x5116848b}, {0xc68, 0x47c00bff},
 {0xc6c, 0x00000036}, {0xc70, 0x2c46000d},
 {0xc74, 0x018610db}, {0xc78, 0x0000001f},
 {0xc7c, 0x00b91612}, {0xc80, 0x24000090},
 {0xc84, 0x20f60000}, {0xc88, 0x24000090},
 {0xc8c, 0x20200000}, {0xc90, 0x00121820},
 {0xc94, 0x00000000}, {0xc98, 0x00121820},
 {0xc9c, 0x00007f7f}, {0xca0, 0x00000000},
 {0xca4, 0x00000080}, {0xca8, 0x00000000},
 {0xcac, 0x00000000}, {0xcb0, 0x00000000},
 {0xcb4, 0x00000000}, {0xcb8, 0x00000000},
 {0xcbc, 0x28000000}, {0xcc0, 0x00000000},
 {0xcc4, 0x00000000}, {0xcc8, 0x00000000},
 {0xccc, 0x00000000}, {0xcd0, 0x00000000},
 {0xcd4, 0x00000000}, {0xcd8, 0x64b22427},
 {0xcdc, 0x00766932}, {0xce0, 0x00222222},
 {0xce4, 0x00000000}, {0xce8, 0x37644302},
 {0xcec, 0x2f97d40c}, {0xd00, 0x00080740},
 {0xd04, 0x00020401}, {0xd08, 0x0000907f},
 {0xd0c, 0x20010201}, {0xd10, 0xa0633333},
 {0xd14, 0x3333bc43}, {0xd18, 0x7a8f5b6b},
 {0xd2c, 0xcc979975}, {0xd30, 0x00000000},
 {0xd34, 0x80608000}, {0xd38, 0x00000000},
 {0xd3c, 0x00027293}, {0xd40, 0x00000000},
 {0xd44, 0x00000000}, {0xd48, 0x00000000},
 {0xd4c, 0x00000000}, {0xd50, 0x6437140a},
 {0xd54, 0x00000000}, {0xd58, 0x00000000},
 {0xd5c, 0x30032064}, {0xd60, 0x4653de68},
 {0xd64, 0x04518a3c}, {0xd68, 0x00002101},
 {0xd6c, 0x2a201c16}, {0xd70, 0x1812362e},
 {0xd74, 0x322c2220}, {0xd78, 0x000e3c24},
 {0xe00, 0x24242424}, {0xe04, 0x24242424},
 {0xe08, 0x03902024}, {0xe10, 0x24242424},
 {0xe14, 0x24242424}, {0xe18, 0x24242424},
 {0xe1c, 0x24242424}, {0xe28, 0x00000000},
 {0xe30, 0x1000dc1f}, {0xe34, 0x10008c1f},
 {0xe38, 0x02140102}, {0xe3c, 0x681604c2},
 {0xe40, 0x01007c00}, {0xe44, 0x01004800},
 {0xe48, 0xfb000000}, {0xe4c, 0x000028d1},
 {0xe50, 0x1000dc1f}, {0xe54, 0x10008c1f},
 {0xe58, 0x02140102}, {0xe5c, 0x28160d05},
 {0xe60, 0x00000008}, {0xe68, 0x001b25a4},
 {0xe6c, 0x631b25a0}, {0xe70, 0x631b25a0},
 {0xe74, 0x081b25a0}, {0xe78, 0x081b25a0},
 {0xe7c, 0x081b25a0}, {0xe80, 0x081b25a0},
 {0xe84, 0x631b25a0}, {0xe88, 0x081b25a0},
 {0xe8c, 0x631b25a0}, {0xed0, 0x631b25a0},
 {0xed4, 0x631b25a0}, {0xed8, 0x631b25a0},
 {0xedc, 0x001b25a0}, {0xee0, 0x001b25a0},
 {0xeec, 0x6b1b25a0}, {0xee8, 0x31555448},
 {0xf14, 0x00000003}, {0xf4c, 0x00000000},
 {0xf00, 0x00000300},
 {0xffff, 0xffffffff},
};

static const struct rtl8xxxu_reg32val rtl8xxx_agc_standard_table[] = {
 {0xc78, 0x7b000001}, {0xc78, 0x7b010001},
 {0xc78, 0x7b020001}, {0xc78, 0x7b030001},
 {0xc78, 0x7b040001}, {0xc78, 0x7b050001},
 {0xc78, 0x7a060001}, {0xc78, 0x79070001},
 {0xc78, 0x78080001}, {0xc78, 0x77090001},
 {0xc78, 0x760a0001}, {0xc78, 0x750b0001},
 {0xc78, 0x740c0001}, {0xc78, 0x730d0001},
 {0xc78, 0x720e0001}, {0xc78, 0x710f0001},
 {0xc78, 0x70100001}, {0xc78, 0x6f110001},
 {0xc78, 0x6e120001}, {0xc78, 0x6d130001},
 {0xc78, 0x6c140001}, {0xc78, 0x6b150001},
 {0xc78, 0x6a160001}, {0xc78, 0x69170001},
 {0xc78, 0x68180001}, {0xc78, 0x67190001},
 {0xc78, 0x661a0001}, {0xc78, 0x651b0001},
 {0xc78, 0x641c0001}, {0xc78, 0x631d0001},
 {0xc78, 0x621e0001}, {0xc78, 0x611f0001},
 {0xc78, 0x60200001}, {0xc78, 0x49210001},
 {0xc78, 0x48220001}, {0xc78, 0x47230001},
 {0xc78, 0x46240001}, {0xc78, 0x45250001},
 {0xc78, 0x44260001}, {0xc78, 0x43270001},
 {0xc78, 0x42280001}, {0xc78, 0x41290001},
 {0xc78, 0x402a0001}, {0xc78, 0x262b0001},
 {0xc78, 0x252c0001}, {0xc78, 0x242d0001},
 {0xc78, 0x232e0001}, {0xc78, 0x222f0001},
 {0xc78, 0x21300001}, {0xc78, 0x20310001},
 {0xc78, 0x06320001}, {0xc78, 0x05330001},
 {0xc78, 0x04340001}, {0xc78, 0x03350001},
 {0xc78, 0x02360001}, {0xc78, 0x01370001},
 {0xc78, 0x00380001}, {0xc78, 0x00390001},
 {0xc78, 0x003a0001}, {0xc78, 0x003b0001},
 {0xc78, 0x003c0001}, {0xc78, 0x003d0001},
 {0xc78, 0x003e0001}, {0xc78, 0x003f0001},
 {0xc78, 0x7b400001}, {0xc78, 0x7b410001},
 {0xc78, 0x7b420001}, {0xc78, 0x7b430001},
 {0xc78, 0x7b440001}, {0xc78, 0x7b450001},
 {0xc78, 0x7a460001}, {0xc78, 0x79470001},
 {0xc78, 0x78480001}, {0xc78, 0x77490001},
 {0xc78, 0x764a0001}, {0xc78, 0x754b0001},
 {0xc78, 0x744c0001}, {0xc78, 0x734d0001},
 {0xc78, 0x724e0001}, {0xc78, 0x714f0001},
 {0xc78, 0x70500001}, {0xc78, 0x6f510001},
 {0xc78, 0x6e520001}, {0xc78, 0x6d530001},
 {0xc78, 0x6c540001}, {0xc78, 0x6b550001},
 {0xc78, 0x6a560001}, {0xc78, 0x69570001},
 {0xc78, 0x68580001}, {0xc78, 0x67590001},
 {0xc78, 0x665a0001}, {0xc78, 0x655b0001},
 {0xc78, 0x645c0001}, {0xc78, 0x635d0001},
 {0xc78, 0x625e0001}, {0xc78, 0x615f0001},
 {0xc78, 0x60600001}, {0xc78, 0x49610001},
 {0xc78, 0x48620001}, {0xc78, 0x47630001},
 {0xc78, 0x46640001}, {0xc78, 0x45650001},
 {0xc78, 0x44660001}, {0xc78, 0x43670001},
 {0xc78, 0x42680001}, {0xc78, 0x41690001},
 {0xc78, 0x406a0001}, {0xc78, 0x266b0001},
 {0xc78, 0x256c0001}, {0xc78, 0x246d0001},
 {0xc78, 0x236e0001}, {0xc78, 0x226f0001},
 {0xc78, 0x21700001}, {0xc78, 0x20710001},
 {0xc78, 0x06720001}, {0xc78, 0x05730001},
 {0xc78, 0x04740001}, {0xc78, 0x03750001},
 {0xc78, 0x02760001}, {0xc78, 0x01770001},
 {0xc78, 0x00780001}, {0xc78, 0x00790001},
 {0xc78, 0x007a0001}, {0xc78, 0x007b0001},
 {0xc78, 0x007c0001}, {0xc78, 0x007d0001},
 {0xc78, 0x007e0001}, {0xc78, 0x007f0001},
 {0xc78, 0x3800001e}, {0xc78, 0x3801001e},
 {0xc78, 0x3802001e}, {0xc78, 0x3803001e},
 {0xc78, 0x3804001e}, {0xc78, 0x3805001e},
 {0xc78, 0x3806001e}, {0xc78, 0x3807001e},
 {0xc78, 0x3808001e}, {0xc78, 0x3c09001e},
 {0xc78, 0x3e0a001e}, {0xc78, 0x400b001e},
 {0xc78, 0x440c001e}, {0xc78, 0x480d001e},
 {0xc78, 0x4c0e001e}, {0xc78, 0x500f001e},
 {0xc78, 0x5210001e}, {0xc78, 0x5611001e},
 {0xc78, 0x5a12001e}, {0xc78, 0x5e13001e},
 {0xc78, 0x6014001e}, {0xc78, 0x6015001e},
 {0xc78, 0x6016001e}, {0xc78, 0x6217001e},
 {0xc78, 0x6218001e}, {0xc78, 0x6219001e},
 {0xc78, 0x621a001e}, {0xc78, 0x621b001e},
 {0xc78, 0x621c001e}, {0xc78, 0x621d001e},
 {0xc78, 0x621e001e}, {0xc78, 0x621f001e},
 {0xffff, 0xffffffff}
};

static const struct rtl8xxxu_reg32val rtl8xxx_agc_highpa_table[] = {
 {0xc78, 0x7b000001}, {0xc78, 0x7b010001},
 {0xc78, 0x7b020001}, {0xc78, 0x7b030001},
 {0xc78, 0x7b040001}, {0xc78, 0x7b050001},
 {0xc78, 0x7b060001}, {0xc78, 0x7b070001},
 {0xc78, 0x7b080001}, {0xc78, 0x7a090001},
 {0xc78, 0x790a0001}, {0xc78, 0x780b0001},
 {0xc78, 0x770c0001}, {0xc78, 0x760d0001},
 {0xc78, 0x750e0001}, {0xc78, 0x740f0001},
 {0xc78, 0x73100001}, {0xc78, 0x72110001},
 {0xc78, 0x71120001}, {0xc78, 0x70130001},
 {0xc78, 0x6f140001}, {0xc78, 0x6e150001},
 {0xc78, 0x6d160001}, {0xc78, 0x6c170001},
 {0xc78, 0x6b180001}, {0xc78, 0x6a190001},
 {0xc78, 0x691a0001}, {0xc78, 0x681b0001},
 {0xc78, 0x671c0001}, {0xc78, 0x661d0001},
 {0xc78, 0x651e0001}, {0xc78, 0x641f0001},
 {0xc78, 0x63200001}, {0xc78, 0x62210001},
 {0xc78, 0x61220001}, {0xc78, 0x60230001},
 {0xc78, 0x46240001}, {0xc78, 0x45250001},
 {0xc78, 0x44260001}, {0xc78, 0x43270001},
 {0xc78, 0x42280001}, {0xc78, 0x41290001},
 {0xc78, 0x402a0001}, {0xc78, 0x262b0001},
 {0xc78, 0x252c0001}, {0xc78, 0x242d0001},
 {0xc78, 0x232e0001}, {0xc78, 0x222f0001},
 {0xc78, 0x21300001}, {0xc78, 0x20310001},
 {0xc78, 0x06320001}, {0xc78, 0x05330001},
 {0xc78, 0x04340001}, {0xc78, 0x03350001},
 {0xc78, 0x02360001}, {0xc78, 0x01370001},
 {0xc78, 0x00380001}, {0xc78, 0x00390001},
 {0xc78, 0x003a0001}, {0xc78, 0x003b0001},
 {0xc78, 0x003c0001}, {0xc78, 0x003d0001},
 {0xc78, 0x003e0001}, {0xc78, 0x003f0001},
 {0xc78, 0x7b400001}, {0xc78, 0x7b410001},
 {0xc78, 0x7b420001}, {0xc78, 0x7b430001},
 {0xc78, 0x7b440001}, {0xc78, 0x7b450001},
 {0xc78, 0x7b460001}, {0xc78, 0x7b470001},
 {0xc78, 0x7b480001}, {0xc78, 0x7a490001},
 {0xc78, 0x794a0001}, {0xc78, 0x784b0001},
 {0xc78, 0x774c0001}, {0xc78, 0x764d0001},
 {0xc78, 0x754e0001}, {0xc78, 0x744f0001},
 {0xc78, 0x73500001}, {0xc78, 0x72510001},
 {0xc78, 0x71520001}, {0xc78, 0x70530001},
 {0xc78, 0x6f540001}, {0xc78, 0x6e550001},
 {0xc78, 0x6d560001}, {0xc78, 0x6c570001},
 {0xc78, 0x6b580001}, {0xc78, 0x6a590001},
 {0xc78, 0x695a0001}, {0xc78, 0x685b0001},
 {0xc78, 0x675c0001}, {0xc78, 0x665d0001},
 {0xc78, 0x655e0001}, {0xc78, 0x645f0001},
 {0xc78, 0x63600001}, {0xc78, 0x62610001},
 {0xc78, 0x61620001}, {0xc78, 0x60630001},
 {0xc78, 0x46640001}, {0xc78, 0x45650001},
 {0xc78, 0x44660001}, {0xc78, 0x43670001},
 {0xc78, 0x42680001}, {0xc78, 0x41690001},
 {0xc78, 0x406a0001}, {0xc78, 0x266b0001},
 {0xc78, 0x256c0001}, {0xc78, 0x246d0001},
 {0xc78, 0x236e0001}, {0xc78, 0x226f0001},
 {0xc78, 0x21700001}, {0xc78, 0x20710001},
 {0xc78, 0x06720001}, {0xc78, 0x05730001},
 {0xc78, 0x04740001}, {0xc78, 0x03750001},
 {0xc78, 0x02760001}, {0xc78, 0x01770001},
 {0xc78, 0x00780001}, {0xc78, 0x00790001},
 {0xc78, 0x007a0001}, {0xc78, 0x007b0001},
 {0xc78, 0x007c0001}, {0xc78, 0x007d0001},
 {0xc78, 0x007e0001}, {0xc78, 0x007f0001},
 {0xc78, 0x3800001e}, {0xc78, 0x3801001e},
 {0xc78, 0x3802001e}, {0xc78, 0x3803001e},
 {0xc78, 0x3804001e}, {0xc78, 0x3805001e},
 {0xc78, 0x3806001e}, {0xc78, 0x3807001e},
 {0xc78, 0x3808001e}, {0xc78, 0x3c09001e},
 {0xc78, 0x3e0a001e}, {0xc78, 0x400b001e},
 {0xc78, 0x440c001e}, {0xc78, 0x480d001e},
 {0xc78, 0x4c0e001e}, {0xc78, 0x500f001e},
 {0xc78, 0x5210001e}, {0xc78, 0x5611001e},
 {0xc78, 0x5a12001e}, {0xc78, 0x5e13001e},
 {0xc78, 0x6014001e}, {0xc78, 0x6015001e},
 {0xc78, 0x6016001e}, {0xc78, 0x6217001e},
 {0xc78, 0x6218001e}, {0xc78, 0x6219001e},
 {0xc78, 0x621a001e}, {0xc78, 0x621b001e},
 {0xc78, 0x621c001e}, {0xc78, 0x621d001e},
 {0xc78, 0x621e001e}, {0xc78, 0x621f001e},
 {0xffff, 0xffffffff}
};

static const struct rtl8xxxu_rfregs rtl8xxxu_rfregs[] = {
 { /* RF_A */
  .hssiparm1 = REG_FPGA0_XA_HSSI_PARM1,
  .hssiparm2 = REG_FPGA0_XA_HSSI_PARM2,
  .lssiparm = REG_FPGA0_XA_LSSI_PARM,
  .hspiread = REG_HSPI_XA_READBACK,
  .lssiread = REG_FPGA0_XA_LSSI_READBACK,
  .rf_sw_ctrl = REG_FPGA0_XA_RF_SW_CTRL,
 },
 { /* RF_B */
  .hssiparm1 = REG_FPGA0_XB_HSSI_PARM1,
  .hssiparm2 = REG_FPGA0_XB_HSSI_PARM2,
  .lssiparm = REG_FPGA0_XB_LSSI_PARM,
  .hspiread = REG_HSPI_XB_READBACK,
  .lssiread = REG_FPGA0_XB_LSSI_READBACK,
  .rf_sw_ctrl = REG_FPGA0_XB_RF_SW_CTRL,
 },
};

const u32 rtl8xxxu_iqk_phy_iq_bb_reg[RTL8XXXU_BB_REGS] = {
 REG_OFDM0_XA_RX_IQ_IMBALANCE,
 REG_OFDM0_XB_RX_IQ_IMBALANCE,
 REG_OFDM0_ENERGY_CCA_THRES,
 REG_OFDM0_AGC_RSSI_TABLE,
 REG_OFDM0_XA_TX_IQ_IMBALANCE,
 REG_OFDM0_XB_TX_IQ_IMBALANCE,
 REG_OFDM0_XC_TX_AFE,
 REG_OFDM0_XD_TX_AFE,
 REG_OFDM0_RX_IQ_EXT_ANTA
};

u8 rtl8xxxu_read8(struct rtl8xxxu_priv *priv, u16 addr)
{
 struct usb_device *udev = priv->udev;
 int len;
 u8 data;

 if (priv->rtl_chip == RTL8710B && addr <= 0xff)
  addr |= 0x8000;

 mutex_lock(&priv->usb_buf_mutex);
 len = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0),
         REALTEK_USB_CMD_REQ, REALTEK_USB_READ,
         addr, 0, &priv->usb_buf.val8, sizeof(u8),
         RTW_USB_CONTROL_MSG_TIMEOUT);
 data = priv->usb_buf.val8;
 mutex_unlock(&priv->usb_buf_mutex);

 if (rtl8xxxu_debug & RTL8XXXU_DEBUG_REG_READ)
  dev_info(&udev->dev, "%s(%04x) = 0x%02x, len %i\n",
    __func__, addr, data, len);
 return data;
}

u16 rtl8xxxu_read16(struct rtl8xxxu_priv *priv, u16 addr)
{
 struct usb_device *udev = priv->udev;
 int len;
 u16 data;

 if (priv->rtl_chip == RTL8710B && addr <= 0xff)
  addr |= 0x8000;

 mutex_lock(&priv->usb_buf_mutex);
 len = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0),
         REALTEK_USB_CMD_REQ, REALTEK_USB_READ,
         addr, 0, &priv->usb_buf.val16, sizeof(u16),
         RTW_USB_CONTROL_MSG_TIMEOUT);
 data = le16_to_cpu(priv->usb_buf.val16);
 mutex_unlock(&priv->usb_buf_mutex);

 if (rtl8xxxu_debug & RTL8XXXU_DEBUG_REG_READ)
  dev_info(&udev->dev, "%s(%04x) = 0x%04x, len %i\n",
    __func__, addr, data, len);
 return data;
}

u32 rtl8xxxu_read32(struct rtl8xxxu_priv *priv, u16 addr)
{
 struct usb_device *udev = priv->udev;
 int len;
 u32 data;

 if (priv->rtl_chip == RTL8710B && addr <= 0xff)
  addr |= 0x8000;

 mutex_lock(&priv->usb_buf_mutex);
 len = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0),
         REALTEK_USB_CMD_REQ, REALTEK_USB_READ,
         addr, 0, &priv->usb_buf.val32, sizeof(u32),
         RTW_USB_CONTROL_MSG_TIMEOUT);
 data = le32_to_cpu(priv->usb_buf.val32);
 mutex_unlock(&priv->usb_buf_mutex);

 if (rtl8xxxu_debug & RTL8XXXU_DEBUG_REG_READ)
  dev_info(&udev->dev, "%s(%04x) = 0x%08x, len %i\n",
    __func__, addr, data, len);
 return data;
}

int rtl8xxxu_write8(struct rtl8xxxu_priv *priv, u16 addr, u8 val)
{
 struct usb_device *udev = priv->udev;
 int ret;

 if (priv->rtl_chip == RTL8710B && addr <= 0xff)
  addr |= 0x8000;

 mutex_lock(&priv->usb_buf_mutex);
 priv->usb_buf.val8 = val;
 ret = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
         REALTEK_USB_CMD_REQ, REALTEK_USB_WRITE,
         addr, 0, &priv->usb_buf.val8, sizeof(u8),
         RTW_USB_CONTROL_MSG_TIMEOUT);

 mutex_unlock(&priv->usb_buf_mutex);

 if (rtl8xxxu_debug & RTL8XXXU_DEBUG_REG_WRITE)
  dev_info(&udev->dev, "%s(%04x) = 0x%02x\n",
    __func__, addr, val);
 return ret;
}

int rtl8xxxu_write16(struct rtl8xxxu_priv *priv, u16 addr, u16 val)
{
 struct usb_device *udev = priv->udev;
 int ret;

 if (priv->rtl_chip == RTL8710B && addr <= 0xff)
  addr |= 0x8000;

 mutex_lock(&priv->usb_buf_mutex);
 priv->usb_buf.val16 = cpu_to_le16(val);
 ret = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
         REALTEK_USB_CMD_REQ, REALTEK_USB_WRITE,
         addr, 0, &priv->usb_buf.val16, sizeof(u16),
         RTW_USB_CONTROL_MSG_TIMEOUT);
 mutex_unlock(&priv->usb_buf_mutex);

 if (rtl8xxxu_debug & RTL8XXXU_DEBUG_REG_WRITE)
  dev_info(&udev->dev, "%s(%04x) = 0x%04x\n",
    __func__, addr, val);
 return ret;
}

int rtl8xxxu_write32(struct rtl8xxxu_priv *priv, u16 addr, u32 val)
{
 struct usb_device *udev = priv->udev;
 int ret;

 if (priv->rtl_chip == RTL8710B && addr <= 0xff)
  addr |= 0x8000;

 mutex_lock(&priv->usb_buf_mutex);
 priv->usb_buf.val32 = cpu_to_le32(val);
 ret = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
         REALTEK_USB_CMD_REQ, REALTEK_USB_WRITE,
         addr, 0, &priv->usb_buf.val32, sizeof(u32),
         RTW_USB_CONTROL_MSG_TIMEOUT);
 mutex_unlock(&priv->usb_buf_mutex);

 if (rtl8xxxu_debug & RTL8XXXU_DEBUG_REG_WRITE)
  dev_info(&udev->dev, "%s(%04x) = 0x%08x\n",
    __func__, addr, val);
 return ret;
}

int rtl8xxxu_write8_set(struct rtl8xxxu_priv *priv, u16 addr, u8 bits)
{
 u8 val8;

 val8 = rtl8xxxu_read8(priv, addr);
 val8 |= bits;
 return rtl8xxxu_write8(priv, addr, val8);
}

int rtl8xxxu_write8_clear(struct rtl8xxxu_priv *priv, u16 addr, u8 bits)
{
 u8 val8;

 val8 = rtl8xxxu_read8(priv, addr);
 val8 &= ~bits;
 return rtl8xxxu_write8(priv, addr, val8);
}

int rtl8xxxu_write16_set(struct rtl8xxxu_priv *priv, u16 addr, u16 bits)
{
 u16 val16;

 val16 = rtl8xxxu_read16(priv, addr);
 val16 |= bits;
 return rtl8xxxu_write16(priv, addr, val16);
}

int rtl8xxxu_write16_clear(struct rtl8xxxu_priv *priv, u16 addr, u16 bits)
{
 u16 val16;

 val16 = rtl8xxxu_read16(priv, addr);
 val16 &= ~bits;
 return rtl8xxxu_write16(priv, addr, val16);
}

int rtl8xxxu_write32_set(struct rtl8xxxu_priv *priv, u16 addr, u32 bits)
{
 u32 val32;

 val32 = rtl8xxxu_read32(priv, addr);
 val32 |= bits;
 return rtl8xxxu_write32(priv, addr, val32);
}

int rtl8xxxu_write32_clear(struct rtl8xxxu_priv *priv, u16 addr, u32 bits)
{
 u32 val32;

 val32 = rtl8xxxu_read32(priv, addr);
 val32 &= ~bits;
 return rtl8xxxu_write32(priv, addr, val32);
}

int rtl8xxxu_write32_mask(struct rtl8xxxu_priv *priv, u16 addr,
     u32 mask, u32 val)
{
 u32 orig, new, shift;

 shift = __ffs(mask);

 orig = rtl8xxxu_read32(priv, addr);
 new = (orig & ~mask) | ((val << shift) & mask);
 return rtl8xxxu_write32(priv, addr, new);
}

int rtl8xxxu_write_rfreg_mask(struct rtl8xxxu_priv *priv,
         enum rtl8xxxu_rfpath path, u8 reg,
         u32 mask, u32 val)
{
 u32 orig, new, shift;

 shift = __ffs(mask);

 orig = rtl8xxxu_read_rfreg(priv, path, reg);
 new = (orig & ~mask) | ((val << shift) & mask);
 return rtl8xxxu_write_rfreg(priv, path, reg, new);
}

static int
rtl8xxxu_writeN(struct rtl8xxxu_priv *priv, u16 addr, u8 *buf, u16 len)
{
 struct usb_device *udev = priv->udev;
 int blocksize = priv->fops->writeN_block_size;
 int ret, i, count, remainder;

 count = len / blocksize;
 remainder = len % blocksize;

 for (i = 0; i < count; i++) {
  ret = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
          REALTEK_USB_CMD_REQ, REALTEK_USB_WRITE,
          addr, 0, buf, blocksize,
          RTW_USB_CONTROL_MSG_TIMEOUT);
  if (ret != blocksize)
   goto write_error;

  addr += blocksize;
  buf += blocksize;
 }

 if (remainder) {
  ret = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
          REALTEK_USB_CMD_REQ, REALTEK_USB_WRITE,
          addr, 0, buf, remainder,
          RTW_USB_CONTROL_MSG_TIMEOUT);
  if (ret != remainder)
   goto write_error;
 }

 return len;

write_error:
 if (rtl8xxxu_debug & RTL8XXXU_DEBUG_REG_WRITE)
  dev_info(&udev->dev,
    "%s: Failed to write block at addr: %04x size: %04x\n",
    __func__, addr, blocksize);
 return -EAGAIN;
}

u32 rtl8xxxu_read_rfreg(struct rtl8xxxu_priv *priv,
   enum rtl8xxxu_rfpath path, u8 reg)
{
 u32 hssia, val32, retval;

 hssia = rtl8xxxu_read32(priv, REG_FPGA0_XA_HSSI_PARM2);
 if (path != RF_A)
  val32 = rtl8xxxu_read32(priv, rtl8xxxu_rfregs[path].hssiparm2);
 else
  val32 = hssia;

 val32 &= ~FPGA0_HSSI_PARM2_ADDR_MASK;
 val32 |= (reg << FPGA0_HSSI_PARM2_ADDR_SHIFT);
 val32 |= FPGA0_HSSI_PARM2_EDGE_READ;
 hssia &= ~FPGA0_HSSI_PARM2_EDGE_READ;
 rtl8xxxu_write32(priv, REG_FPGA0_XA_HSSI_PARM2, hssia);

 udelay(10);

 rtl8xxxu_write32(priv, rtl8xxxu_rfregs[path].hssiparm2, val32);
 udelay(100);

 hssia |= FPGA0_HSSI_PARM2_EDGE_READ;
 rtl8xxxu_write32(priv, REG_FPGA0_XA_HSSI_PARM2, hssia);
 udelay(10);

 val32 = rtl8xxxu_read32(priv, rtl8xxxu_rfregs[path].hssiparm1);
 if (val32 & FPGA0_HSSI_PARM1_PI)
  retval = rtl8xxxu_read32(priv, rtl8xxxu_rfregs[path].hspiread);
 else
  retval = rtl8xxxu_read32(priv, rtl8xxxu_rfregs[path].lssiread);

 retval &= 0xfffff;

 if (rtl8xxxu_debug & RTL8XXXU_DEBUG_RFREG_READ)
  dev_info(&priv->udev->dev, "%s(%02x) = 0x%06x\n",
    __func__, reg, retval);
 return retval;
}

/*
 * The RTL8723BU driver indicates that registers 0xb2 and 0xb6 can
 * have write issues in high temperature conditions. We may have to
 * retry writing them.
 */

int rtl8xxxu_write_rfreg(struct rtl8xxxu_priv *priv,
    enum rtl8xxxu_rfpath path, u8 reg, u32 data)
{
 int ret, retval;
 u32 dataaddr, val32;

 if (rtl8xxxu_debug & RTL8XXXU_DEBUG_RFREG_WRITE)
  dev_info(&priv->udev->dev, "%s(%02x) = 0x%06x\n",
    __func__, reg, data);

 data &= FPGA0_LSSI_PARM_DATA_MASK;
 dataaddr = (reg << FPGA0_LSSI_PARM_ADDR_SHIFT) | data;

 if (priv->rtl_chip == RTL8192E) {
  val32 = rtl8xxxu_read32(priv, REG_FPGA0_POWER_SAVE);
  val32 &= ~0x20000;
  rtl8xxxu_write32(priv, REG_FPGA0_POWER_SAVE, val32);
 }

 /* Use XB for path B */
 ret = rtl8xxxu_write32(priv, rtl8xxxu_rfregs[path].lssiparm, dataaddr);
 if (ret != sizeof(dataaddr))
  retval = -EIO;
 else
  retval = 0;

 udelay(1);

 if (priv->rtl_chip == RTL8192E) {
  val32 = rtl8xxxu_read32(priv, REG_FPGA0_POWER_SAVE);
  val32 |= 0x20000;
  rtl8xxxu_write32(priv, REG_FPGA0_POWER_SAVE, val32);
 }

 return retval;
}

static int
rtl8xxxu_gen1_h2c_cmd(struct rtl8xxxu_priv *priv, struct h2c_cmd *h2c, int len)
{
 struct device *dev = &priv->udev->dev;
 int mbox_nr, retry, retval = 0;
 int mbox_reg, mbox_ext_reg;
 u8 val8;

 mutex_lock(&priv->h2c_mutex);

 mbox_nr = priv->next_mbox;
 mbox_reg = REG_HMBOX_0 + (mbox_nr * 4);
 mbox_ext_reg = REG_HMBOX_EXT_0 + (mbox_nr * 2);

 /*
 * MBOX ready?
 */

 retry = 100;
 do {
  val8 = rtl8xxxu_read8(priv, REG_HMTFR);
  if (!(val8 & BIT(mbox_nr)))
   break;
 } while (retry--);

 if (!retry) {
  dev_info(dev, "%s: Mailbox busy\n", __func__);
  retval = -EBUSY;
  goto error;
 }

 /*
 * Need to swap as it's being swapped again by rtl8xxxu_write16/32()
 */

 if (len > sizeof(u32)) {
  rtl8xxxu_write16(priv, mbox_ext_reg, le16_to_cpu(h2c->raw.ext));
  if (rtl8xxxu_debug & RTL8XXXU_DEBUG_H2C)
   dev_info(dev, "H2C_EXT %04x\n",
     le16_to_cpu(h2c->raw.ext));
 }
 rtl8xxxu_write32(priv, mbox_reg, le32_to_cpu(h2c->raw.data));
 if (rtl8xxxu_debug & RTL8XXXU_DEBUG_H2C)
  dev_info(dev, "H2C %08x\n", le32_to_cpu(h2c->raw.data));

 priv->next_mbox = (mbox_nr + 1) % H2C_MAX_MBOX;

error:
 mutex_unlock(&priv->h2c_mutex);
 return retval;
}

int
rtl8xxxu_gen2_h2c_cmd(struct rtl8xxxu_priv *priv, struct h2c_cmd *h2c, int len)
{
 struct device *dev = &priv->udev->dev;
 int mbox_nr, retry, retval = 0;
 int mbox_reg, mbox_ext_reg;
 u8 val8;

 mutex_lock(&priv->h2c_mutex);

 mbox_nr = priv->next_mbox;
 mbox_reg = REG_HMBOX_0 + (mbox_nr * 4);
 mbox_ext_reg = REG_HMBOX_EXT0_8723B + (mbox_nr * 4);

 /*
 * MBOX ready?
 */

 retry = 100;
 do {
  val8 = rtl8xxxu_read8(priv, REG_HMTFR);
  if (!(val8 & BIT(mbox_nr)))
   break;
 } while (retry--);

 if (!retry) {
  dev_info(dev, "%s: Mailbox busy\n", __func__);
  retval = -EBUSY;
  goto error;
 }

 /*
 * Need to swap as it's being swapped again by rtl8xxxu_write16/32()
 */

 if (len > sizeof(u32)) {
  rtl8xxxu_write32(priv, mbox_ext_reg,
     le32_to_cpu(h2c->raw_wide.ext));
  if (rtl8xxxu_debug & RTL8XXXU_DEBUG_H2C)
   dev_info(dev, "H2C_EXT %08x\n",
     le32_to_cpu(h2c->raw_wide.ext));
 }
 rtl8xxxu_write32(priv, mbox_reg, le32_to_cpu(h2c->raw.data));
 if (rtl8xxxu_debug & RTL8XXXU_DEBUG_H2C)
  dev_info(dev, "H2C %08x\n", le32_to_cpu(h2c->raw.data));

 priv->next_mbox = (mbox_nr + 1) % H2C_MAX_MBOX;

error:
 mutex_unlock(&priv->h2c_mutex);
 return retval;
}

void rtl8xxxu_gen1_enable_rf(struct rtl8xxxu_priv *priv)
{
 u8 val8;
 u32 val32;

 val8 = rtl8xxxu_read8(priv, REG_SPS0_CTRL);
 val8 |= BIT(0) | BIT(3);
 rtl8xxxu_write8(priv, REG_SPS0_CTRL, val8);

 val32 = rtl8xxxu_read32(priv, REG_FPGA0_XAB_RF_PARM);
 val32 &= ~(BIT(4) | BIT(5));
 val32 |= BIT(3);
 if (priv->rf_paths == 2) {
  val32 &= ~(BIT(20) | BIT(21));
  val32 |= BIT(19);
 }
 rtl8xxxu_write32(priv, REG_FPGA0_XAB_RF_PARM, val32);

 val32 = rtl8xxxu_read32(priv, REG_OFDM0_TRX_PATH_ENABLE);
 val32 &= ~OFDM_RF_PATH_TX_MASK;
 if (priv->tx_paths == 2)
  val32 |= OFDM_RF_PATH_TX_A | OFDM_RF_PATH_TX_B;
 else if (priv->rtl_chip == RTL8192C || priv->rtl_chip == RTL8191C)
  val32 |= OFDM_RF_PATH_TX_B;
 else
  val32 |= OFDM_RF_PATH_TX_A;
 rtl8xxxu_write32(priv, REG_OFDM0_TRX_PATH_ENABLE, val32);

 val32 = rtl8xxxu_read32(priv, REG_FPGA0_RF_MODE);
 val32 &= ~FPGA_RF_MODE_JAPAN;
 rtl8xxxu_write32(priv, REG_FPGA0_RF_MODE, val32);

 if (priv->rf_paths == 2)
  rtl8xxxu_write32(priv, REG_RX_WAIT_CCA, 0x63db25a0);
 else
  rtl8xxxu_write32(priv, REG_RX_WAIT_CCA, 0x631b25a0);

 rtl8xxxu_write_rfreg(priv, RF_A, RF6052_REG_AC, 0x32d95);
 if (priv->rf_paths == 2)
  rtl8xxxu_write_rfreg(priv, RF_B, RF6052_REG_AC, 0x32d95);

 rtl8xxxu_write8(priv, REG_TXPAUSE, 0x00);
}

void rtl8xxxu_gen1_disable_rf(struct rtl8xxxu_priv *priv)
{
 u8 sps0;
 u32 val32;

 sps0 = rtl8xxxu_read8(priv, REG_SPS0_CTRL);

 /* RF RX code for preamble power saving */
 val32 = rtl8xxxu_read32(priv, REG_FPGA0_XAB_RF_PARM);
 val32 &= ~(BIT(3) | BIT(4) | BIT(5));
 if (priv->rf_paths == 2)
  val32 &= ~(BIT(19) | BIT(20) | BIT(21));
 rtl8xxxu_write32(priv, REG_FPGA0_XAB_RF_PARM, val32);

 /* Disable TX for four paths */
 val32 = rtl8xxxu_read32(priv, REG_OFDM0_TRX_PATH_ENABLE);
 val32 &= ~OFDM_RF_PATH_TX_MASK;
 rtl8xxxu_write32(priv, REG_OFDM0_TRX_PATH_ENABLE, val32);

 /* Enable power saving */
 val32 = rtl8xxxu_read32(priv, REG_FPGA0_RF_MODE);
 val32 |= FPGA_RF_MODE_JAPAN;
 rtl8xxxu_write32(priv, REG_FPGA0_RF_MODE, val32);

 /* AFE control register to power down bits [30:22] */
 if (priv->rf_paths == 2)
  rtl8xxxu_write32(priv, REG_RX_WAIT_CCA, 0x00db25a0);
 else
  rtl8xxxu_write32(priv, REG_RX_WAIT_CCA, 0x001b25a0);

 /* Power down RF module */
 rtl8xxxu_write_rfreg(priv, RF_A, RF6052_REG_AC, 0);
 if (priv->rf_paths == 2)
  rtl8xxxu_write_rfreg(priv, RF_B, RF6052_REG_AC, 0);

 sps0 &= ~(BIT(0) | BIT(3));
 rtl8xxxu_write8(priv, REG_SPS0_CTRL, sps0);
}

static void rtl8xxxu_stop_tx_beacon(struct rtl8xxxu_priv *priv)
{
 u8 val8;

 val8 = rtl8xxxu_read8(priv, REG_FWHW_TXQ_CTRL + 2);
 val8 &= ~BIT(6);
 rtl8xxxu_write8(priv, REG_FWHW_TXQ_CTRL + 2, val8);

 rtl8xxxu_write8(priv, REG_TBTT_PROHIBIT + 1, 0x64);
 val8 = rtl8xxxu_read8(priv, REG_TBTT_PROHIBIT + 2);
 val8 &= ~BIT(0);
 rtl8xxxu_write8(priv, REG_TBTT_PROHIBIT + 2, val8);
}

static void rtl8xxxu_start_tx_beacon(struct rtl8xxxu_priv *priv)
{
 u8 val8;

 val8 = rtl8xxxu_read8(priv, REG_FWHW_TXQ_CTRL + 2);
 val8 |= EN_BCNQ_DL >> 16;
 rtl8xxxu_write8(priv, REG_FWHW_TXQ_CTRL + 2, val8);

 rtl8xxxu_write8(priv, REG_TBTT_PROHIBIT + 1, 0x80);
 val8 = rtl8xxxu_read8(priv, REG_TBTT_PROHIBIT + 2);
 val8 &= 0xF0;
 rtl8xxxu_write8(priv, REG_TBTT_PROHIBIT + 2, val8);
}


/*
 * The rtl8723a has 3 channel groups for its efuse settings. It only
 * supports the 2.4GHz band, so channels 1 - 14:
 *  group 0: channels 1 - 3
 *  group 1: channels 4 - 9
 *  group 2: channels 10 - 14
 *
 * Note: We index from 0 in the code
 */

static int rtl8xxxu_gen1_channel_to_group(int channel)
{
 int group;

 if (channel < 4)
  group = 0;
 else if (channel < 10)
  group = 1;
 else
  group = 2;

 return group;
}

/*
 * Valid for rtl8723bu and rtl8192eu
 */

int rtl8xxxu_gen2_channel_to_group(int channel)
{
 int group;

 if (channel < 3)
  group = 0;
 else if (channel < 6)
  group = 1;
 else if (channel < 9)
  group = 2;
 else if (channel < 12)
  group = 3;
 else
  group = 4;

 return group;
}

void rtl8xxxu_gen1_config_channel(struct ieee80211_hw *hw)
{
 struct rtl8xxxu_priv *priv = hw->priv;
 u32 val32, rsr;
 u8 val8, opmode;
 bool ht = true;
 int sec_ch_above, channel;
 int i;

 opmode = rtl8xxxu_read8(priv, REG_BW_OPMODE);
 rsr = rtl8xxxu_read32(priv, REG_RESPONSE_RATE_SET);
 channel = hw->conf.chandef.chan->hw_value;

 switch (hw->conf.chandef.width) {
 case NL80211_CHAN_WIDTH_20_NOHT:
  ht = false;
  fallthrough;
 case NL80211_CHAN_WIDTH_20:
  opmode |= BW_OPMODE_20MHZ;
  rtl8xxxu_write8(priv, REG_BW_OPMODE, opmode);

  val32 = rtl8xxxu_read32(priv, REG_FPGA0_RF_MODE);
  val32 &= ~FPGA_RF_MODE;
  rtl8xxxu_write32(priv, REG_FPGA0_RF_MODE, val32);

  val32 = rtl8xxxu_read32(priv, REG_FPGA1_RF_MODE);
  val32 &= ~FPGA_RF_MODE;
  rtl8xxxu_write32(priv, REG_FPGA1_RF_MODE, val32);

  val32 = rtl8xxxu_read32(priv, REG_FPGA0_ANALOG2);
  val32 |= FPGA0_ANALOG2_20MHZ;
  rtl8xxxu_write32(priv, REG_FPGA0_ANALOG2, val32);
  break;
 case NL80211_CHAN_WIDTH_40:
  if (hw->conf.chandef.center_freq1 >
      hw->conf.chandef.chan->center_freq) {
   sec_ch_above = 1;
   channel += 2;
  } else {
   sec_ch_above = 0;
   channel -= 2;
  }

  opmode &= ~BW_OPMODE_20MHZ;
  rtl8xxxu_write8(priv, REG_BW_OPMODE, opmode);
  rsr &= ~RSR_RSC_BANDWIDTH_40M;
  if (sec_ch_above)
   rsr |= RSR_RSC_UPPER_SUB_CHANNEL;
  else
   rsr |= RSR_RSC_LOWER_SUB_CHANNEL;
  rtl8xxxu_write32(priv, REG_RESPONSE_RATE_SET, rsr);

  val32 = rtl8xxxu_read32(priv, REG_FPGA0_RF_MODE);
  val32 |= FPGA_RF_MODE;
  rtl8xxxu_write32(priv, REG_FPGA0_RF_MODE, val32);

  val32 = rtl8xxxu_read32(priv, REG_FPGA1_RF_MODE);
  val32 |= FPGA_RF_MODE;
  rtl8xxxu_write32(priv, REG_FPGA1_RF_MODE, val32);

  /*
 * Set Control channel to upper or lower. These settings
 * are required only for 40MHz
 */

  val32 = rtl8xxxu_read32(priv, REG_CCK0_SYSTEM);
  val32 &= ~CCK0_SIDEBAND;
  if (!sec_ch_above)
   val32 |= CCK0_SIDEBAND;
  rtl8xxxu_write32(priv, REG_CCK0_SYSTEM, val32);

  val32 = rtl8xxxu_read32(priv, REG_OFDM1_LSTF);
  val32 &= ~OFDM_LSTF_PRIME_CH_MASK; /* 0xc00 */
  if (sec_ch_above)
   val32 |= OFDM_LSTF_PRIME_CH_LOW;
  else
   val32 |= OFDM_LSTF_PRIME_CH_HIGH;
  rtl8xxxu_write32(priv, REG_OFDM1_LSTF, val32);

  val32 = rtl8xxxu_read32(priv, REG_FPGA0_ANALOG2);
  val32 &= ~FPGA0_ANALOG2_20MHZ;
  rtl8xxxu_write32(priv, REG_FPGA0_ANALOG2, val32);

  val32 = rtl8xxxu_read32(priv, REG_FPGA0_POWER_SAVE);
  val32 &= ~(FPGA0_PS_LOWER_CHANNEL | FPGA0_PS_UPPER_CHANNEL);
  if (sec_ch_above)
   val32 |= FPGA0_PS_UPPER_CHANNEL;
  else
   val32 |= FPGA0_PS_LOWER_CHANNEL;
  rtl8xxxu_write32(priv, REG_FPGA0_POWER_SAVE, val32);
  break;

 default:
  break;
 }

 for (i = RF_A; i < priv->rf_paths; i++) {
  val32 = rtl8xxxu_read_rfreg(priv, i, RF6052_REG_MODE_AG);
  val32 &= ~MODE_AG_CHANNEL_MASK;
  val32 |= channel;
  rtl8xxxu_write_rfreg(priv, i, RF6052_REG_MODE_AG, val32);
 }

 if (ht)
  val8 = 0x0e;
 else
  val8 = 0x0a;

 rtl8xxxu_write8(priv, REG_SIFS_CCK + 1, val8);
 rtl8xxxu_write8(priv, REG_SIFS_OFDM + 1, val8);

 rtl8xxxu_write16(priv, REG_R2T_SIFS, 0x0808);
 rtl8xxxu_write16(priv, REG_T2T_SIFS, 0x0a0a);

 for (i = RF_A; i < priv->rf_paths; i++) {
  val32 = rtl8xxxu_read_rfreg(priv, i, RF6052_REG_MODE_AG);
  if (hw->conf.chandef.width == NL80211_CHAN_WIDTH_40)
   val32 &= ~MODE_AG_CHANNEL_20MHZ;
  else
   val32 |= MODE_AG_CHANNEL_20MHZ;
  rtl8xxxu_write_rfreg(priv, i, RF6052_REG_MODE_AG, val32);
 }
}

void rtl8xxxu_gen2_config_channel(struct ieee80211_hw *hw)
{
 struct rtl8xxxu_priv *priv = hw->priv;
 u32 val32;
 u8 val8, subchannel;
 u16 rf_mode_bw;
 bool ht = true;
 int sec_ch_above, channel;
 int i;

 rf_mode_bw = rtl8xxxu_read16(priv, REG_WMAC_TRXPTCL_CTL);
 rf_mode_bw &= ~WMAC_TRXPTCL_CTL_BW_MASK;
 channel = hw->conf.chandef.chan->hw_value;

/* Hack */
 subchannel = 0;

 switch (hw->conf.chandef.width) {
 case NL80211_CHAN_WIDTH_20_NOHT:
  ht = false;
  fallthrough;
 case NL80211_CHAN_WIDTH_20:
  rf_mode_bw |= WMAC_TRXPTCL_CTL_BW_20;
  subchannel = 0;

  val32 = rtl8xxxu_read32(priv, REG_FPGA0_RF_MODE);
  val32 &= ~FPGA_RF_MODE;
  rtl8xxxu_write32(priv, REG_FPGA0_RF_MODE, val32);

  val32 = rtl8xxxu_read32(priv, REG_FPGA1_RF_MODE);
  val32 &= ~FPGA_RF_MODE;
  rtl8xxxu_write32(priv, REG_FPGA1_RF_MODE, val32);

  val32 = rtl8xxxu_read32(priv, REG_OFDM0_TX_PSDO_NOISE_WEIGHT);
  val32 &= ~(BIT(30) | BIT(31));
  rtl8xxxu_write32(priv, REG_OFDM0_TX_PSDO_NOISE_WEIGHT, val32);

  break;
 case NL80211_CHAN_WIDTH_40:
  rf_mode_bw |= WMAC_TRXPTCL_CTL_BW_40;

  if (hw->conf.chandef.center_freq1 >
      hw->conf.chandef.chan->center_freq) {
   sec_ch_above = 1;
   channel += 2;
  } else {
   sec_ch_above = 0;
   channel -= 2;
  }

  val32 = rtl8xxxu_read32(priv, REG_FPGA0_RF_MODE);
  val32 |= FPGA_RF_MODE;
  rtl8xxxu_write32(priv, REG_FPGA0_RF_MODE, val32);

  val32 = rtl8xxxu_read32(priv, REG_FPGA1_RF_MODE);
  val32 |= FPGA_RF_MODE;
  rtl8xxxu_write32(priv, REG_FPGA1_RF_MODE, val32);

  /*
 * Set Control channel to upper or lower. These settings
 * are required only for 40MHz
 */

  val32 = rtl8xxxu_read32(priv, REG_CCK0_SYSTEM);
  val32 &= ~CCK0_SIDEBAND;
  if (!sec_ch_above)
   val32 |= CCK0_SIDEBAND;
  rtl8xxxu_write32(priv, REG_CCK0_SYSTEM, val32);

  val32 = rtl8xxxu_read32(priv, REG_OFDM1_LSTF);
  val32 &= ~OFDM_LSTF_PRIME_CH_MASK; /* 0xc00 */
  if (sec_ch_above)
   val32 |= OFDM_LSTF_PRIME_CH_LOW;
  else
   val32 |= OFDM_LSTF_PRIME_CH_HIGH;
  rtl8xxxu_write32(priv, REG_OFDM1_LSTF, val32);

  val32 = rtl8xxxu_read32(priv, REG_FPGA0_POWER_SAVE);
  val32 &= ~(FPGA0_PS_LOWER_CHANNEL | FPGA0_PS_UPPER_CHANNEL);
  if (sec_ch_above)
   val32 |= FPGA0_PS_UPPER_CHANNEL;
  else
   val32 |= FPGA0_PS_LOWER_CHANNEL;
  rtl8xxxu_write32(priv, REG_FPGA0_POWER_SAVE, val32);
  break;
 case NL80211_CHAN_WIDTH_80:
  rf_mode_bw |= WMAC_TRXPTCL_CTL_BW_80;
  break;
 default:
  break;
 }

 for (i = RF_A; i < priv->rf_paths; i++) {
  val32 = rtl8xxxu_read_rfreg(priv, i, RF6052_REG_MODE_AG);
  val32 &= ~MODE_AG_CHANNEL_MASK;
  val32 |= channel;
  rtl8xxxu_write_rfreg(priv, i, RF6052_REG_MODE_AG, val32);
 }

 rtl8xxxu_write16(priv, REG_WMAC_TRXPTCL_CTL, rf_mode_bw);
 rtl8xxxu_write8(priv, REG_DATA_SUBCHANNEL, subchannel);

 if (ht)
  val8 = 0x0e;
 else
  val8 = 0x0a;

 rtl8xxxu_write8(priv, REG_SIFS_CCK + 1, val8);
 rtl8xxxu_write8(priv, REG_SIFS_OFDM + 1, val8);

 rtl8xxxu_write16(priv, REG_R2T_SIFS, 0x0808);
 rtl8xxxu_write16(priv, REG_T2T_SIFS, 0x0a0a);

 for (i = RF_A; i < priv->rf_paths; i++) {
  val32 = rtl8xxxu_read_rfreg(priv, i, RF6052_REG_MODE_AG);
  val32 &= ~MODE_AG_BW_MASK;
  switch(hw->conf.chandef.width) {
  case NL80211_CHAN_WIDTH_80:
   val32 |= MODE_AG_BW_80MHZ_8723B;
   break;
  case NL80211_CHAN_WIDTH_40:
   val32 |= MODE_AG_BW_40MHZ_8723B;
   break;
  default:
   val32 |= MODE_AG_BW_20MHZ_8723B;
   break;
  }
  rtl8xxxu_write_rfreg(priv, i, RF6052_REG_MODE_AG, val32);
 }
}

void
rtl8xxxu_gen1_set_tx_power(struct rtl8xxxu_priv *priv, int channel, bool ht40)
{
 struct rtl8xxxu_power_base *power_base = priv->power_base;
 u8 cck[RTL8723A_MAX_RF_PATHS], ofdm[RTL8723A_MAX_RF_PATHS];
 u8 ofdmbase[RTL8723A_MAX_RF_PATHS], mcsbase[RTL8723A_MAX_RF_PATHS];
 u32 val32, ofdm_a, ofdm_b, mcs_a, mcs_b;
 u8 val8, base;
 int group, i;

 group = rtl8xxxu_gen1_channel_to_group(channel);

 cck[0] = priv->cck_tx_power_index_A[group];
 cck[1] = priv->cck_tx_power_index_B[group];

 if (priv->hi_pa) {
  if (cck[0] > 0x20)
   cck[0] = 0x20;
  if (cck[1] > 0x20)
   cck[1] = 0x20;
 }

 ofdm[0] = priv->ht40_1s_tx_power_index_A[group];
 ofdm[1] = priv->ht40_1s_tx_power_index_B[group];

 ofdmbase[0] = ofdm[0] + priv->ofdm_tx_power_index_diff[group].a;
 ofdmbase[1] = ofdm[1] + priv->ofdm_tx_power_index_diff[group].b;

 mcsbase[0] = ofdm[0];
 mcsbase[1] = ofdm[1];
 if (!ht40) {
  mcsbase[0] += priv->ht20_tx_power_index_diff[group].a;
  mcsbase[1] += priv->ht20_tx_power_index_diff[group].b;
 }

 if (priv->tx_paths > 1) {
  if (ofdm[0] > priv->ht40_2s_tx_power_index_diff[group].a)
   ofdm[0] -=  priv->ht40_2s_tx_power_index_diff[group].a;
  if (ofdm[1] > priv->ht40_2s_tx_power_index_diff[group].b)
   ofdm[1] -=  priv->ht40_2s_tx_power_index_diff[group].b;
 }

 if (rtl8xxxu_debug & RTL8XXXU_DEBUG_CHANNEL)
  dev_info(&priv->udev->dev,
    "%s: Setting TX power CCK A: %02x, "
    "CCK B: %02x, OFDM A: %02x, OFDM B: %02x\n",
    __func__, cck[0], cck[1], ofdm[0], ofdm[1]);

 for (i = 0; i < RTL8723A_MAX_RF_PATHS; i++) {
  if (cck[i] > RF6052_MAX_TX_PWR)
   cck[i] = RF6052_MAX_TX_PWR;
  if (ofdm[i] > RF6052_MAX_TX_PWR)
   ofdm[i] = RF6052_MAX_TX_PWR;
 }

 val32 = rtl8xxxu_read32(priv, REG_TX_AGC_A_CCK1_MCS32);
 val32 &= 0xffff00ff;
 val32 |= (cck[0] << 8);
 rtl8xxxu_write32(priv, REG_TX_AGC_A_CCK1_MCS32, val32);

 val32 = rtl8xxxu_read32(priv, REG_TX_AGC_B_CCK11_A_CCK2_11);
 val32 &= 0xff;
 val32 |= ((cck[0] << 8) | (cck[0] << 16) | (cck[0] << 24));
 rtl8xxxu_write32(priv, REG_TX_AGC_B_CCK11_A_CCK2_11, val32);

 val32 = rtl8xxxu_read32(priv, REG_TX_AGC_B_CCK11_A_CCK2_11);
 val32 &= 0xffffff00;
 val32 |= cck[1];
 rtl8xxxu_write32(priv, REG_TX_AGC_B_CCK11_A_CCK2_11, val32);

 val32 = rtl8xxxu_read32(priv, REG_TX_AGC_B_CCK1_55_MCS32);
 val32 &= 0xff;
 val32 |= ((cck[1] << 8) | (cck[1] << 16) | (cck[1] << 24));
 rtl8xxxu_write32(priv, REG_TX_AGC_B_CCK1_55_MCS32, val32);

 ofdm_a = ofdmbase[0] | ofdmbase[0] << 8 |
  ofdmbase[0] << 16 | ofdmbase[0] << 24;
 ofdm_b = ofdmbase[1] | ofdmbase[1] << 8 |
  ofdmbase[1] << 16 | ofdmbase[1] << 24;

 rtl8xxxu_write32(priv, REG_TX_AGC_A_RATE18_06,
    ofdm_a + power_base->reg_0e00);
 rtl8xxxu_write32(priv, REG_TX_AGC_B_RATE18_06,
    ofdm_b + power_base->reg_0830);

 rtl8xxxu_write32(priv, REG_TX_AGC_A_RATE54_24,
    ofdm_a + power_base->reg_0e04);
 rtl8xxxu_write32(priv, REG_TX_AGC_B_RATE54_24,
    ofdm_b + power_base->reg_0834);

 mcs_a = mcsbase[0] | mcsbase[0] << 8 |
  mcsbase[0] << 16 | mcsbase[0] << 24;
 mcs_b = mcsbase[1] | mcsbase[1] << 8 |
  mcsbase[1] << 16 | mcsbase[1] << 24;

 rtl8xxxu_write32(priv, REG_TX_AGC_A_MCS03_MCS00,
    mcs_a + power_base->reg_0e10);
 rtl8xxxu_write32(priv, REG_TX_AGC_B_MCS03_MCS00,
    mcs_b + power_base->reg_083c);

 rtl8xxxu_write32(priv, REG_TX_AGC_A_MCS07_MCS04,
    mcs_a + power_base->reg_0e14);
 rtl8xxxu_write32(priv, REG_TX_AGC_B_MCS07_MCS04,
    mcs_b + power_base->reg_0848);

 rtl8xxxu_write32(priv, REG_TX_AGC_A_MCS11_MCS08,
    mcs_a + power_base->reg_0e18);
 rtl8xxxu_write32(priv, REG_TX_AGC_B_MCS11_MCS08,
    mcs_b + power_base->reg_084c);

 rtl8xxxu_write32(priv, REG_TX_AGC_A_MCS15_MCS12,
    mcs_a + power_base->reg_0e1c);
 val8 = u32_get_bits(mcs_a + power_base->reg_0e1c, 0xff000000);
 for (i = 0; i < 3; i++) {
  base = i != 2 ? 8 : 6;
  val8 = max_t(int, val8 - base, 0);
  rtl8xxxu_write8(priv, REG_OFDM0_XC_TX_IQ_IMBALANCE + i, val8);
 }

 rtl8xxxu_write32(priv, REG_TX_AGC_B_MCS15_MCS12,
    mcs_b + power_base->reg_0868);
 val8 = u32_get_bits(mcs_b + power_base->reg_0868, 0xff000000);
 for (i = 0; i < 3; i++) {
  base = i != 2 ? 8 : 6;
  val8 = max_t(int, val8 - base, 0);
  rtl8xxxu_write8(priv, REG_OFDM0_XD_TX_IQ_IMBALANCE + i, val8);
 }
}

static void rtl8xxxu_set_linktype(struct rtl8xxxu_priv *priv,
      enum nl80211_iftype linktype, int port_num)
{
 u8 val8, type;

 switch (linktype) {
 case NL80211_IFTYPE_UNSPECIFIED:
  type = MSR_LINKTYPE_NONE;
  break;
 case NL80211_IFTYPE_ADHOC:
  type = MSR_LINKTYPE_ADHOC;
  break;
 case NL80211_IFTYPE_STATION:
  type = MSR_LINKTYPE_STATION;
  break;
 case NL80211_IFTYPE_AP:
  type = MSR_LINKTYPE_AP;
  break;
 default:
  return;
 }

 switch (port_num) {
 case 0:
  val8 = rtl8xxxu_read8(priv, REG_MSR) & 0x0c;
  val8 |= type;
  break;
 case 1:
  val8 = rtl8xxxu_read8(priv, REG_MSR) & 0x03;
  val8 |= type << 2;
  break;
 default:
  return;
 }

 rtl8xxxu_write8(priv, REG_MSR, val8);
}

static void
rtl8xxxu_set_retry(struct rtl8xxxu_priv *priv, u16 short_retry, u16 long_retry)
{
 u16 val16;

 val16 = ((short_retry << RETRY_LIMIT_SHORT_SHIFT) &
   RETRY_LIMIT_SHORT_MASK) |
  ((long_retry << RETRY_LIMIT_LONG_SHIFT) &
   RETRY_LIMIT_LONG_MASK);

 rtl8xxxu_write16(priv, REG_RETRY_LIMIT, val16);
}

static void
rtl8xxxu_set_spec_sifs(struct rtl8xxxu_priv *priv, u16 cck, u16 ofdm)
{
 u16 val16;

 val16 = ((cck << SPEC_SIFS_CCK_SHIFT) & SPEC_SIFS_CCK_MASK) |
  ((ofdm << SPEC_SIFS_OFDM_SHIFT) & SPEC_SIFS_OFDM_MASK);

 rtl8xxxu_write16(priv, REG_SPEC_SIFS, val16);
}

static void rtl8xxxu_print_chipinfo(struct rtl8xxxu_priv *priv)
{
 struct device *dev = &priv->udev->dev;
 char cut = 'A' + priv->chip_cut;

 dev_info(dev,
   "RTL%s rev %c (%s) romver %d, %iT%iR, TX queues %i, WiFi=%i, BT=%i, GPS=%i, HI PA=%i\n",
   priv->chip_name, cut, priv->chip_vendor, priv->rom_rev,
   priv->tx_paths, priv->rx_paths, priv->ep_tx_count,
   priv->has_wifi, priv->has_bluetooth, priv->has_gps,
   priv->hi_pa);

 dev_info(dev, "RTL%s MAC: %pM\n", priv->chip_name, priv->mac_addr);
}

void rtl8xxxu_identify_vendor_1bit(struct rtl8xxxu_priv *priv, u32 vendor)
{
 if (vendor) {
  strscpy(priv->chip_vendor, "UMC"sizeof(priv->chip_vendor));
  priv->vendor_umc = 1;
 } else {
  strscpy(priv->chip_vendor, "TSMC"sizeof(priv->chip_vendor));
 }
}

void rtl8xxxu_identify_vendor_2bits(struct rtl8xxxu_priv *priv, u32 vendor)
{
 switch (vendor) {
 case SYS_CFG_VENDOR_ID_TSMC:
  strscpy(priv->chip_vendor, "TSMC"sizeof(priv->chip_vendor));
  break;
 case SYS_CFG_VENDOR_ID_SMIC:
  strscpy(priv->chip_vendor, "SMIC"sizeof(priv->chip_vendor));
  priv->vendor_smic = 1;
  break;
 case SYS_CFG_VENDOR_ID_UMC:
  strscpy(priv->chip_vendor, "UMC"sizeof(priv->chip_vendor));
  priv->vendor_umc = 1;
  break;
 default:
  strscpy(priv->chip_vendor, "unknown"sizeof(priv->chip_vendor));
 }
}

void rtl8xxxu_config_endpoints_sie(struct rtl8xxxu_priv *priv)
{
 u16 val16;

 val16 = rtl8xxxu_read16(priv, REG_NORMAL_SIE_EP_TX);

 if (val16 & NORMAL_SIE_EP_TX_HIGH_MASK) {
  priv->ep_tx_high_queue = 1;
  priv->ep_tx_count++;
 }

 if (val16 & NORMAL_SIE_EP_TX_NORMAL_MASK) {
  priv->ep_tx_normal_queue = 1;
  priv->ep_tx_count++;
 }

 if (val16 & NORMAL_SIE_EP_TX_LOW_MASK) {
  priv->ep_tx_low_queue = 1;
  priv->ep_tx_count++;
 }
}

int rtl8xxxu_config_endpoints_no_sie(struct rtl8xxxu_priv *priv)
{
 struct device *dev = &priv->udev->dev;

 switch (priv->nr_out_eps) {
 case 6:
 case 5:
 case 4:
 case 3:
  priv->ep_tx_low_queue = 1;
  priv->ep_tx_count++;
  fallthrough;
 case 2:
  priv->ep_tx_normal_queue = 1;
  priv->ep_tx_count++;
  fallthrough;
 case 1:
  priv->ep_tx_high_queue = 1;
  priv->ep_tx_count++;
  break;
 default:
  dev_info(dev, "Unsupported USB TX end-points\n");
  return -ENOTSUPP;
 }

 return 0;
}

int
rtl8xxxu_read_efuse8(struct rtl8xxxu_priv *priv, u16 offset, u8 *data)
{
 int i;
 u8 val8;
 u32 val32;

 /* Write Address */
 rtl8xxxu_write8(priv, REG_EFUSE_CTRL + 1, offset & 0xff);
 val8 = rtl8xxxu_read8(priv, REG_EFUSE_CTRL + 2);
 val8 &= 0xfc;
 val8 |= (offset >> 8) & 0x03;
 rtl8xxxu_write8(priv, REG_EFUSE_CTRL + 2, val8);

 val8 = rtl8xxxu_read8(priv, REG_EFUSE_CTRL + 3);
 rtl8xxxu_write8(priv, REG_EFUSE_CTRL + 3, val8 & 0x7f);

 /* Poll for data read */
 val32 = rtl8xxxu_read32(priv, REG_EFUSE_CTRL);
 for (i = 0; i < RTL8XXXU_MAX_REG_POLL; i++) {
  val32 = rtl8xxxu_read32(priv, REG_EFUSE_CTRL);
  if (val32 & BIT(31))
   break;
 }

 if (i == RTL8XXXU_MAX_REG_POLL)
  return -EIO;

 udelay(50);
 val32 = rtl8xxxu_read32(priv, REG_EFUSE_CTRL);

 *data = val32 & 0xff;
 return 0;
}

int rtl8xxxu_read_efuse(struct rtl8xxxu_priv *priv)
{
 struct device *dev = &priv->udev->dev;
 int i, ret = 0;
 u8 val8, word_mask, header, extheader;
 u16 val16, efuse_addr, offset;
 u32 val32;

 val16 = rtl8xxxu_read16(priv, REG_9346CR);
 if (val16 & EEPROM_ENABLE)
  priv->has_eeprom = 1;
 if (val16 & EEPROM_BOOT)
  priv->boot_eeprom = 1;

 if (priv->is_multi_func) {
  val32 = rtl8xxxu_read32(priv, REG_EFUSE_TEST);
  val32 = (val32 & ~EFUSE_SELECT_MASK) | EFUSE_WIFI_SELECT;
  rtl8xxxu_write32(priv, REG_EFUSE_TEST, val32);
 }

 dev_dbg(dev, "Booting from %s\n",
  priv->boot_eeprom ? "EEPROM" : "EFUSE");

 rtl8xxxu_write8(priv, REG_EFUSE_ACCESS, EFUSE_ACCESS_ENABLE);

 /*  1.2V Power: From VDDON with Power Cut(0x0000[15]), default valid */
 val16 = rtl8xxxu_read16(priv, REG_SYS_ISO_CTRL);
 if (!(val16 & SYS_ISO_PWC_EV12V)) {
  val16 |= SYS_ISO_PWC_EV12V;
  rtl8xxxu_write16(priv, REG_SYS_ISO_CTRL, val16);
 }
 /*  Reset: 0x0000[28], default valid */
 val16 = rtl8xxxu_read16(priv, REG_SYS_FUNC);
 if (!(val16 & SYS_FUNC_ELDR)) {
  val16 |= SYS_FUNC_ELDR;
  rtl8xxxu_write16(priv, REG_SYS_FUNC, val16);
 }

 /*
 * Clock: Gated(0x0008[5]) 8M(0x0008[1]) clock from ANA, default valid
 */

 val16 = rtl8xxxu_read16(priv, REG_SYS_CLKR);
 if (!(val16 & SYS_CLK_LOADER_ENABLE) || !(val16 & SYS_CLK_ANA8M)) {
  val16 |= (SYS_CLK_LOADER_ENABLE | SYS_CLK_ANA8M);
  rtl8xxxu_write16(priv, REG_SYS_CLKR, val16);
 }

 /* Default value is 0xff */
 memset(priv->efuse_wifi.raw, 0xff, EFUSE_MAP_LEN);

 efuse_addr = 0;
 while (efuse_addr < EFUSE_REAL_CONTENT_LEN_8723A) {
  u16 map_addr;

  ret = rtl8xxxu_read_efuse8(priv, efuse_addr++, &header);
  if (ret || header == 0xff)
   goto exit;

  if ((header & 0x1f) == 0x0f) { /* extended header */
   offset = (header & 0xe0) >> 5;

   ret = rtl8xxxu_read_efuse8(priv, efuse_addr++,
         &extheader);
   if (ret)
    goto exit;
   /* All words disabled */
   if ((extheader & 0x0f) == 0x0f)
    continue;

   offset |= ((extheader & 0xf0) >> 1);
   word_mask = extheader & 0x0f;
  } else {
   offset = (header >> 4) & 0x0f;
   word_mask = header & 0x0f;
  }

  /* Get word enable value from PG header */

  /* We have 8 bits to indicate validity */
  map_addr = offset * 8;
  for (i = 0; i < EFUSE_MAX_WORD_UNIT; i++) {
   /* Check word enable condition in the section */
   if (word_mask & BIT(i)) {
    map_addr += 2;
    continue;
   }

   ret = rtl8xxxu_read_efuse8(priv, efuse_addr++, &val8);
   if (ret)
    goto exit;
   if (map_addr >= EFUSE_MAP_LEN - 1) {
    dev_warn(dev, "%s: Illegal map_addr (%04x), "
      "efuse corrupt!\n",
      __func__, map_addr);
    ret = -EINVAL;
    goto exit;
   }
   priv->efuse_wifi.raw[map_addr++] = val8;

   ret = rtl8xxxu_read_efuse8(priv, efuse_addr++, &val8);
   if (ret)
    goto exit;
   priv->efuse_wifi.raw[map_addr++] = val8;
  }
 }

exit:
 rtl8xxxu_write8(priv, REG_EFUSE_ACCESS, EFUSE_ACCESS_DISABLE);

 return ret;
}

static void rtl8xxxu_dump_efuse(struct rtl8xxxu_priv *priv)
{
 dev_info(&priv->udev->dev,
   "Dumping efuse for RTL%s (0x%02x bytes):\n",
   priv->chip_name, EFUSE_MAP_LEN);

 print_hex_dump(KERN_INFO, "", DUMP_PREFIX_OFFSET, 16, 1,
         priv->efuse_wifi.raw, EFUSE_MAP_LEN, true);
}

void rtl8xxxu_reset_8051(struct rtl8xxxu_priv *priv)
{
 u8 val8;
 u16 sys_func;

 val8 = rtl8xxxu_read8(priv, REG_RSV_CTRL + 1);
 val8 &= ~BIT(0);
 rtl8xxxu_write8(priv, REG_RSV_CTRL + 1, val8);

 sys_func = rtl8xxxu_read16(priv, REG_SYS_FUNC);
 sys_func &= ~SYS_FUNC_CPU_ENABLE;
 rtl8xxxu_write16(priv, REG_SYS_FUNC, sys_func);

 val8 = rtl8xxxu_read8(priv, REG_RSV_CTRL + 1);
 val8 |= BIT(0);
 rtl8xxxu_write8(priv, REG_RSV_CTRL + 1, val8);

 sys_func |= SYS_FUNC_CPU_ENABLE;
 rtl8xxxu_write16(priv, REG_SYS_FUNC, sys_func);
}

static int rtl8xxxu_start_firmware(struct rtl8xxxu_priv *priv)
{
 struct device *dev = &priv->udev->dev;
 u16 reg_mcu_fw_dl;
 int ret = 0, i;
 u32 val32;

 if (priv->rtl_chip == RTL8710B)
  reg_mcu_fw_dl = REG_8051FW_CTRL_V1_8710B;
 else
  reg_mcu_fw_dl = REG_MCU_FW_DL;

 /* Poll checksum report */
 for (i = 0; i < RTL8XXXU_FIRMWARE_POLL_MAX; i++) {
  val32 = rtl8xxxu_read32(priv, reg_mcu_fw_dl);
  if (val32 & MCU_FW_DL_CSUM_REPORT)
   break;
 }

 if (i == RTL8XXXU_FIRMWARE_POLL_MAX) {
  dev_warn(dev, "Firmware checksum poll timed out\n");
  ret = -EAGAIN;
  goto exit;
 }

 val32 = rtl8xxxu_read32(priv, reg_mcu_fw_dl);
 val32 |= MCU_FW_DL_READY;
 val32 &= ~MCU_WINT_INIT_READY;
 rtl8xxxu_write32(priv, reg_mcu_fw_dl, val32);

 /*
 * Reset the 8051 in order for the firmware to start running,
 * otherwise it won't come up on the 8192eu
 */

 priv->fops->reset_8051(priv);

 /* Wait for firmware to become ready */
 for (i = 0; i < RTL8XXXU_FIRMWARE_POLL_MAX; i++) {
  val32 = rtl8xxxu_read32(priv, reg_mcu_fw_dl);
  if (val32 & MCU_WINT_INIT_READY)
   break;

  udelay(100);
 }

 if (i == RTL8XXXU_FIRMWARE_POLL_MAX) {
  dev_warn(dev, "Firmware failed to start\n");
  ret = -EAGAIN;
  goto exit;
 }

 /*
 * Init H2C command
 */

 if (priv->fops->init_reg_hmtfr)
  rtl8xxxu_write8(priv, REG_HMTFR, 0x0f);
exit:
 return ret;
}

static int rtl8xxxu_download_firmware(struct rtl8xxxu_priv *priv)
{
 int pages, remainder, i, ret;
 u16 reg_fw_start_address;
 u16 reg_mcu_fw_dl;
 u8 val8;
 u16 val16;
 u32 val32;
 u8 *fwptr;

 if (priv->rtl_chip == RTL8192F)
  reg_fw_start_address = REG_FW_START_ADDRESS_8192F;
 else
  reg_fw_start_address = REG_FW_START_ADDRESS;

 if (priv->rtl_chip == RTL8710B) {
  reg_mcu_fw_dl = REG_8051FW_CTRL_V1_8710B;
 } else {
  reg_mcu_fw_dl = REG_MCU_FW_DL;

  val8 = rtl8xxxu_read8(priv, REG_SYS_FUNC + 1);
  val8 |= 4;
  rtl8xxxu_write8(priv, REG_SYS_FUNC + 1, val8);

  /* 8051 enable */
  val16 = rtl8xxxu_read16(priv, REG_SYS_FUNC);
  val16 |= SYS_FUNC_CPU_ENABLE;
  rtl8xxxu_write16(priv, REG_SYS_FUNC, val16);
 }

 val8 = rtl8xxxu_read8(priv, reg_mcu_fw_dl);
 if (val8 & MCU_FW_RAM_SEL) {
  dev_info(&priv->udev->dev,
    "Firmware is already running, resetting the MCU.\n");
  rtl8xxxu_write8(priv, reg_mcu_fw_dl, 0x00);
  priv->fops->reset_8051(priv);
 }

 /* MCU firmware download enable */
 val8 = rtl8xxxu_read8(priv, reg_mcu_fw_dl);
 val8 |= MCU_FW_DL_ENABLE;
 rtl8xxxu_write8(priv, reg_mcu_fw_dl, val8);

 /* 8051 reset */
 val32 = rtl8xxxu_read32(priv, reg_mcu_fw_dl);
 val32 &= ~BIT(19);
 rtl8xxxu_write32(priv, reg_mcu_fw_dl, val32);

 if (priv->rtl_chip == RTL8710B) {
  /* We must set 0x8090[8]=1 before download FW. */
  val8 = rtl8xxxu_read8(priv, reg_mcu_fw_dl + 1);
  val8 |= BIT(0);
  rtl8xxxu_write8(priv, reg_mcu_fw_dl + 1, val8);
 }

 /* Reset firmware download checksum */
 val8 = rtl8xxxu_read8(priv, reg_mcu_fw_dl);
 val8 |= MCU_FW_DL_CSUM_REPORT;
 rtl8xxxu_write8(priv, reg_mcu_fw_dl, val8);

 pages = priv->fw_size / RTL_FW_PAGE_SIZE;
 remainder = priv->fw_size % RTL_FW_PAGE_SIZE;

 fwptr = priv->fw_data->data;

 for (i = 0; i < pages; i++) {
  val8 = rtl8xxxu_read8(priv, reg_mcu_fw_dl + 2) & 0xF8;
  val8 |= i;
  rtl8xxxu_write8(priv, reg_mcu_fw_dl + 2, val8);

  ret = rtl8xxxu_writeN(priv, reg_fw_start_address,
          fwptr, RTL_FW_PAGE_SIZE);
  if (ret != RTL_FW_PAGE_SIZE) {
   ret = -EAGAIN;
   goto fw_abort;
  }

  fwptr += RTL_FW_PAGE_SIZE;
 }

 if (remainder) {
  val8 = rtl8xxxu_read8(priv, reg_mcu_fw_dl + 2) & 0xF8;
  val8 |= i;
  rtl8xxxu_write8(priv, reg_mcu_fw_dl + 2, val8);
  ret = rtl8xxxu_writeN(priv, reg_fw_start_address,
          fwptr, remainder);
  if (ret != remainder) {
   ret = -EAGAIN;
   goto fw_abort;
  }
 }

 ret = 0;
fw_abort:
 /* MCU firmware download disable */
 val16 = rtl8xxxu_read16(priv, reg_mcu_fw_dl);
 val16 &= ~MCU_FW_DL_ENABLE;
 rtl8xxxu_write16(priv, reg_mcu_fw_dl, val16);

 return ret;
}

int rtl8xxxu_load_firmware(struct rtl8xxxu_priv *priv, const char *fw_name)
{
 struct device *dev = &priv->udev->dev;
 const struct firmware *fw;
 int ret = 0;
 u16 signature;

 dev_info(dev, "%s: Loading firmware %s\n", DRIVER_NAME, fw_name);
 if (request_firmware(&fw, fw_name, &priv->udev->dev)) {
  dev_warn(dev, "request_firmware(%s) failed\n", fw_name);
  ret = -EAGAIN;
  goto exit;
 }
 if (!fw) {
  dev_warn(dev, "Firmware data not available\n");
  ret = -EINVAL;
  goto exit;
 }

 priv->fw_data = kmemdup(fw->data, fw->size, GFP_KERNEL);
 if (!priv->fw_data) {
  ret = -ENOMEM;
  goto exit;
 }
 priv->fw_size = fw->size - sizeof(struct rtl8xxxu_firmware_header);

 signature = le16_to_cpu(priv->fw_data->signature);
 switch (signature & 0xfff0) {
 case 0x92e0:
 case 0x92c0:
 case 0x88e0:
 case 0x88c0:
 case 0x5300:
 case 0x2300:
 case 0x88f0:
 case 0x10b0:
 case 0x92f0:
  break;
 default:
  ret = -EINVAL;
  dev_warn(dev, "%s: Invalid firmware signature: 0x%04x\n",
    __func__, signature);
 }

 dev_info(dev, "Firmware revision %i.%i (signature 0x%04x)\n",
   le16_to_cpu(priv->fw_data->major_version),
   priv->fw_data->minor_version, signature);

exit:
 release_firmware(fw);
 return ret;
}

void rtl8xxxu_firmware_self_reset(struct rtl8xxxu_priv *priv)
{
 u16 val16;
 int i = 100;

 /* Inform 8051 to perform reset */
 rtl8xxxu_write8(priv, REG_HMTFR + 3, 0x20);

 for (i = 100; i > 0; i--) {
  val16 = rtl8xxxu_read16(priv, REG_SYS_FUNC);

  if (!(val16 & SYS_FUNC_CPU_ENABLE)) {
   dev_dbg(&priv->udev->dev,
    "%s: Firmware self reset success!\n", __func__);
   break;
  }
  udelay(50);
 }

 if (!i) {
  /* Force firmware reset */
  val16 = rtl8xxxu_read16(priv, REG_SYS_FUNC);
  val16 &= ~SYS_FUNC_CPU_ENABLE;
  rtl8xxxu_write16(priv, REG_SYS_FUNC, val16);
 }
}

static int
rtl8xxxu_init_mac(struct rtl8xxxu_priv *priv)
{
 const struct rtl8xxxu_reg8val *array = priv->fops->mactable;
 int i, ret;
 u16 reg;
 u8 val;

 for (i = 0; ; i++) {
  reg = array[i].reg;
  val = array[i].val;

  if (reg == 0xffff && val == 0xff)
   break;

  ret = rtl8xxxu_write8(priv, reg, val);
  if (ret != 1) {
   dev_warn(&priv->udev->dev,
     "Failed to initialize MAC "
     "(reg: %04x, val %02x)\n", reg, val);
   return -EAGAIN;
  }
 }

 switch (priv->rtl_chip) {
--> --------------------

--> maximum size reached

--> --------------------

Messung V0.5
C=98 H=87 G=92

¤ Dauer der Verarbeitung: 0.45 Sekunden  (vorverarbeitet)  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.






                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Software

     Produkte
     Quellcodebibliothek

Aktivitäten

     Artikel über Sicherheit
     Anleitung zur Aktivierung von SSL

Muße

     Gedichte
     Musik
     Bilder

Jenseits des Üblichen ....

Besucherstatistik

Besucherstatistik

Monitoring

Montastic status badge