18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci 48c2ecf20Sopenharmony_ci Broadcom B43 wireless driver 58c2ecf20Sopenharmony_ci Common PHY routines 68c2ecf20Sopenharmony_ci 78c2ecf20Sopenharmony_ci Copyright (c) 2005 Martin Langer <martin-langer@gmx.de>, 88c2ecf20Sopenharmony_ci Copyright (c) 2005-2007 Stefano Brivio <stefano.brivio@polimi.it> 98c2ecf20Sopenharmony_ci Copyright (c) 2005-2008 Michael Buesch <m@bues.ch> 108c2ecf20Sopenharmony_ci Copyright (c) 2005, 2006 Danny van Dyk <kugelfang@gentoo.org> 118c2ecf20Sopenharmony_ci Copyright (c) 2005, 2006 Andreas Jaggi <andreas.jaggi@waterwave.ch> 128c2ecf20Sopenharmony_ci 138c2ecf20Sopenharmony_ci 148c2ecf20Sopenharmony_ci*/ 158c2ecf20Sopenharmony_ci 168c2ecf20Sopenharmony_ci#include "phy_common.h" 178c2ecf20Sopenharmony_ci#include "phy_g.h" 188c2ecf20Sopenharmony_ci#include "phy_a.h" 198c2ecf20Sopenharmony_ci#include "phy_n.h" 208c2ecf20Sopenharmony_ci#include "phy_lp.h" 218c2ecf20Sopenharmony_ci#include "phy_ht.h" 228c2ecf20Sopenharmony_ci#include "phy_lcn.h" 238c2ecf20Sopenharmony_ci#include "phy_ac.h" 248c2ecf20Sopenharmony_ci#include "b43.h" 258c2ecf20Sopenharmony_ci#include "main.h" 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_ciint b43_phy_allocate(struct b43_wldev *dev) 298c2ecf20Sopenharmony_ci{ 308c2ecf20Sopenharmony_ci struct b43_phy *phy = &(dev->phy); 318c2ecf20Sopenharmony_ci int err; 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_ci phy->ops = NULL; 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_ci switch (phy->type) { 368c2ecf20Sopenharmony_ci case B43_PHYTYPE_G: 378c2ecf20Sopenharmony_ci#ifdef CONFIG_B43_PHY_G 388c2ecf20Sopenharmony_ci phy->ops = &b43_phyops_g; 398c2ecf20Sopenharmony_ci#endif 408c2ecf20Sopenharmony_ci break; 418c2ecf20Sopenharmony_ci case B43_PHYTYPE_N: 428c2ecf20Sopenharmony_ci#ifdef CONFIG_B43_PHY_N 438c2ecf20Sopenharmony_ci phy->ops = &b43_phyops_n; 448c2ecf20Sopenharmony_ci#endif 458c2ecf20Sopenharmony_ci break; 468c2ecf20Sopenharmony_ci case B43_PHYTYPE_LP: 478c2ecf20Sopenharmony_ci#ifdef CONFIG_B43_PHY_LP 488c2ecf20Sopenharmony_ci phy->ops = &b43_phyops_lp; 498c2ecf20Sopenharmony_ci#endif 508c2ecf20Sopenharmony_ci break; 518c2ecf20Sopenharmony_ci case B43_PHYTYPE_HT: 528c2ecf20Sopenharmony_ci#ifdef CONFIG_B43_PHY_HT 538c2ecf20Sopenharmony_ci phy->ops = &b43_phyops_ht; 548c2ecf20Sopenharmony_ci#endif 558c2ecf20Sopenharmony_ci break; 568c2ecf20Sopenharmony_ci case B43_PHYTYPE_LCN: 578c2ecf20Sopenharmony_ci#ifdef CONFIG_B43_PHY_LCN 588c2ecf20Sopenharmony_ci phy->ops = &b43_phyops_lcn; 598c2ecf20Sopenharmony_ci#endif 608c2ecf20Sopenharmony_ci break; 618c2ecf20Sopenharmony_ci case B43_PHYTYPE_AC: 628c2ecf20Sopenharmony_ci#ifdef CONFIG_B43_PHY_AC 638c2ecf20Sopenharmony_ci phy->ops = &b43_phyops_ac; 648c2ecf20Sopenharmony_ci#endif 658c2ecf20Sopenharmony_ci break; 668c2ecf20Sopenharmony_ci } 678c2ecf20Sopenharmony_ci if (B43_WARN_ON(!phy->ops)) 688c2ecf20Sopenharmony_ci return -ENODEV; 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_ci err = phy->ops->allocate(dev); 718c2ecf20Sopenharmony_ci if (err) 728c2ecf20Sopenharmony_ci phy->ops = NULL; 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_ci return err; 758c2ecf20Sopenharmony_ci} 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_civoid b43_phy_free(struct b43_wldev *dev) 788c2ecf20Sopenharmony_ci{ 798c2ecf20Sopenharmony_ci dev->phy.ops->free(dev); 808c2ecf20Sopenharmony_ci dev->phy.ops = NULL; 818c2ecf20Sopenharmony_ci} 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_ciint b43_phy_init(struct b43_wldev *dev) 848c2ecf20Sopenharmony_ci{ 858c2ecf20Sopenharmony_ci struct b43_phy *phy = &dev->phy; 868c2ecf20Sopenharmony_ci const struct b43_phy_operations *ops = phy->ops; 878c2ecf20Sopenharmony_ci int err; 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ci /* During PHY init we need to use some channel. On the first init this 908c2ecf20Sopenharmony_ci * function is called *before* b43_op_config, so our pointer is NULL. 918c2ecf20Sopenharmony_ci */ 928c2ecf20Sopenharmony_ci if (!phy->chandef) { 938c2ecf20Sopenharmony_ci phy->chandef = &dev->wl->hw->conf.chandef; 948c2ecf20Sopenharmony_ci phy->channel = phy->chandef->chan->hw_value; 958c2ecf20Sopenharmony_ci } 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_ci phy->ops->switch_analog(dev, true); 988c2ecf20Sopenharmony_ci b43_software_rfkill(dev, false); 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci err = ops->init(dev); 1018c2ecf20Sopenharmony_ci if (err) { 1028c2ecf20Sopenharmony_ci b43err(dev->wl, "PHY init failed\n"); 1038c2ecf20Sopenharmony_ci goto err_block_rf; 1048c2ecf20Sopenharmony_ci } 1058c2ecf20Sopenharmony_ci phy->do_full_init = false; 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ci err = b43_switch_channel(dev, phy->channel); 1088c2ecf20Sopenharmony_ci if (err) { 1098c2ecf20Sopenharmony_ci b43err(dev->wl, "PHY init: Channel switch to default failed\n"); 1108c2ecf20Sopenharmony_ci goto err_phy_exit; 1118c2ecf20Sopenharmony_ci } 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ci return 0; 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_cierr_phy_exit: 1168c2ecf20Sopenharmony_ci phy->do_full_init = true; 1178c2ecf20Sopenharmony_ci if (ops->exit) 1188c2ecf20Sopenharmony_ci ops->exit(dev); 1198c2ecf20Sopenharmony_cierr_block_rf: 1208c2ecf20Sopenharmony_ci b43_software_rfkill(dev, true); 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_ci return err; 1238c2ecf20Sopenharmony_ci} 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_civoid b43_phy_exit(struct b43_wldev *dev) 1268c2ecf20Sopenharmony_ci{ 1278c2ecf20Sopenharmony_ci const struct b43_phy_operations *ops = dev->phy.ops; 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_ci b43_software_rfkill(dev, true); 1308c2ecf20Sopenharmony_ci dev->phy.do_full_init = true; 1318c2ecf20Sopenharmony_ci if (ops->exit) 1328c2ecf20Sopenharmony_ci ops->exit(dev); 1338c2ecf20Sopenharmony_ci} 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_cibool b43_has_hardware_pctl(struct b43_wldev *dev) 1368c2ecf20Sopenharmony_ci{ 1378c2ecf20Sopenharmony_ci if (!dev->phy.hardware_power_control) 1388c2ecf20Sopenharmony_ci return false; 1398c2ecf20Sopenharmony_ci if (!dev->phy.ops->supports_hwpctl) 1408c2ecf20Sopenharmony_ci return false; 1418c2ecf20Sopenharmony_ci return dev->phy.ops->supports_hwpctl(dev); 1428c2ecf20Sopenharmony_ci} 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_civoid b43_radio_lock(struct b43_wldev *dev) 1458c2ecf20Sopenharmony_ci{ 1468c2ecf20Sopenharmony_ci u32 macctl; 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_ci#if B43_DEBUG 1498c2ecf20Sopenharmony_ci B43_WARN_ON(dev->phy.radio_locked); 1508c2ecf20Sopenharmony_ci dev->phy.radio_locked = true; 1518c2ecf20Sopenharmony_ci#endif 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ci macctl = b43_read32(dev, B43_MMIO_MACCTL); 1548c2ecf20Sopenharmony_ci macctl |= B43_MACCTL_RADIOLOCK; 1558c2ecf20Sopenharmony_ci b43_write32(dev, B43_MMIO_MACCTL, macctl); 1568c2ecf20Sopenharmony_ci /* Commit the write and wait for the firmware 1578c2ecf20Sopenharmony_ci * to finish any radio register access. */ 1588c2ecf20Sopenharmony_ci b43_read32(dev, B43_MMIO_MACCTL); 1598c2ecf20Sopenharmony_ci udelay(10); 1608c2ecf20Sopenharmony_ci} 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_civoid b43_radio_unlock(struct b43_wldev *dev) 1638c2ecf20Sopenharmony_ci{ 1648c2ecf20Sopenharmony_ci u32 macctl; 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_ci#if B43_DEBUG 1678c2ecf20Sopenharmony_ci B43_WARN_ON(!dev->phy.radio_locked); 1688c2ecf20Sopenharmony_ci dev->phy.radio_locked = false; 1698c2ecf20Sopenharmony_ci#endif 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_ci /* Commit any write */ 1728c2ecf20Sopenharmony_ci b43_read16(dev, B43_MMIO_PHY_VER); 1738c2ecf20Sopenharmony_ci /* unlock */ 1748c2ecf20Sopenharmony_ci macctl = b43_read32(dev, B43_MMIO_MACCTL); 1758c2ecf20Sopenharmony_ci macctl &= ~B43_MACCTL_RADIOLOCK; 1768c2ecf20Sopenharmony_ci b43_write32(dev, B43_MMIO_MACCTL, macctl); 1778c2ecf20Sopenharmony_ci} 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_civoid b43_phy_lock(struct b43_wldev *dev) 1808c2ecf20Sopenharmony_ci{ 1818c2ecf20Sopenharmony_ci#if B43_DEBUG 1828c2ecf20Sopenharmony_ci B43_WARN_ON(dev->phy.phy_locked); 1838c2ecf20Sopenharmony_ci dev->phy.phy_locked = true; 1848c2ecf20Sopenharmony_ci#endif 1858c2ecf20Sopenharmony_ci B43_WARN_ON(dev->dev->core_rev < 3); 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_ci if (!b43_is_mode(dev->wl, NL80211_IFTYPE_AP)) 1888c2ecf20Sopenharmony_ci b43_power_saving_ctl_bits(dev, B43_PS_AWAKE); 1898c2ecf20Sopenharmony_ci} 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_civoid b43_phy_unlock(struct b43_wldev *dev) 1928c2ecf20Sopenharmony_ci{ 1938c2ecf20Sopenharmony_ci#if B43_DEBUG 1948c2ecf20Sopenharmony_ci B43_WARN_ON(!dev->phy.phy_locked); 1958c2ecf20Sopenharmony_ci dev->phy.phy_locked = false; 1968c2ecf20Sopenharmony_ci#endif 1978c2ecf20Sopenharmony_ci B43_WARN_ON(dev->dev->core_rev < 3); 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_ci if (!b43_is_mode(dev->wl, NL80211_IFTYPE_AP)) 2008c2ecf20Sopenharmony_ci b43_power_saving_ctl_bits(dev, 0); 2018c2ecf20Sopenharmony_ci} 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_cistatic inline void assert_mac_suspended(struct b43_wldev *dev) 2048c2ecf20Sopenharmony_ci{ 2058c2ecf20Sopenharmony_ci if (!B43_DEBUG) 2068c2ecf20Sopenharmony_ci return; 2078c2ecf20Sopenharmony_ci if ((b43_status(dev) >= B43_STAT_INITIALIZED) && 2088c2ecf20Sopenharmony_ci (dev->mac_suspended <= 0)) { 2098c2ecf20Sopenharmony_ci b43dbg(dev->wl, "PHY/RADIO register access with " 2108c2ecf20Sopenharmony_ci "enabled MAC.\n"); 2118c2ecf20Sopenharmony_ci dump_stack(); 2128c2ecf20Sopenharmony_ci } 2138c2ecf20Sopenharmony_ci} 2148c2ecf20Sopenharmony_ci 2158c2ecf20Sopenharmony_ciu16 b43_radio_read(struct b43_wldev *dev, u16 reg) 2168c2ecf20Sopenharmony_ci{ 2178c2ecf20Sopenharmony_ci assert_mac_suspended(dev); 2188c2ecf20Sopenharmony_ci dev->phy.writes_counter = 0; 2198c2ecf20Sopenharmony_ci return dev->phy.ops->radio_read(dev, reg); 2208c2ecf20Sopenharmony_ci} 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_civoid b43_radio_write(struct b43_wldev *dev, u16 reg, u16 value) 2238c2ecf20Sopenharmony_ci{ 2248c2ecf20Sopenharmony_ci assert_mac_suspended(dev); 2258c2ecf20Sopenharmony_ci if (b43_bus_host_is_pci(dev->dev) && 2268c2ecf20Sopenharmony_ci ++dev->phy.writes_counter > B43_MAX_WRITES_IN_ROW) { 2278c2ecf20Sopenharmony_ci b43_read32(dev, B43_MMIO_MACCTL); 2288c2ecf20Sopenharmony_ci dev->phy.writes_counter = 1; 2298c2ecf20Sopenharmony_ci } 2308c2ecf20Sopenharmony_ci dev->phy.ops->radio_write(dev, reg, value); 2318c2ecf20Sopenharmony_ci} 2328c2ecf20Sopenharmony_ci 2338c2ecf20Sopenharmony_civoid b43_radio_mask(struct b43_wldev *dev, u16 offset, u16 mask) 2348c2ecf20Sopenharmony_ci{ 2358c2ecf20Sopenharmony_ci b43_radio_write16(dev, offset, 2368c2ecf20Sopenharmony_ci b43_radio_read16(dev, offset) & mask); 2378c2ecf20Sopenharmony_ci} 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_civoid b43_radio_set(struct b43_wldev *dev, u16 offset, u16 set) 2408c2ecf20Sopenharmony_ci{ 2418c2ecf20Sopenharmony_ci b43_radio_write16(dev, offset, 2428c2ecf20Sopenharmony_ci b43_radio_read16(dev, offset) | set); 2438c2ecf20Sopenharmony_ci} 2448c2ecf20Sopenharmony_ci 2458c2ecf20Sopenharmony_civoid b43_radio_maskset(struct b43_wldev *dev, u16 offset, u16 mask, u16 set) 2468c2ecf20Sopenharmony_ci{ 2478c2ecf20Sopenharmony_ci b43_radio_write16(dev, offset, 2488c2ecf20Sopenharmony_ci (b43_radio_read16(dev, offset) & mask) | set); 2498c2ecf20Sopenharmony_ci} 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_cibool b43_radio_wait_value(struct b43_wldev *dev, u16 offset, u16 mask, 2528c2ecf20Sopenharmony_ci u16 value, int delay, int timeout) 2538c2ecf20Sopenharmony_ci{ 2548c2ecf20Sopenharmony_ci u16 val; 2558c2ecf20Sopenharmony_ci int i; 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_ci for (i = 0; i < timeout; i += delay) { 2588c2ecf20Sopenharmony_ci val = b43_radio_read(dev, offset); 2598c2ecf20Sopenharmony_ci if ((val & mask) == value) 2608c2ecf20Sopenharmony_ci return true; 2618c2ecf20Sopenharmony_ci udelay(delay); 2628c2ecf20Sopenharmony_ci } 2638c2ecf20Sopenharmony_ci return false; 2648c2ecf20Sopenharmony_ci} 2658c2ecf20Sopenharmony_ci 2668c2ecf20Sopenharmony_ciu16 b43_phy_read(struct b43_wldev *dev, u16 reg) 2678c2ecf20Sopenharmony_ci{ 2688c2ecf20Sopenharmony_ci assert_mac_suspended(dev); 2698c2ecf20Sopenharmony_ci dev->phy.writes_counter = 0; 2708c2ecf20Sopenharmony_ci 2718c2ecf20Sopenharmony_ci if (dev->phy.ops->phy_read) 2728c2ecf20Sopenharmony_ci return dev->phy.ops->phy_read(dev, reg); 2738c2ecf20Sopenharmony_ci 2748c2ecf20Sopenharmony_ci b43_write16f(dev, B43_MMIO_PHY_CONTROL, reg); 2758c2ecf20Sopenharmony_ci return b43_read16(dev, B43_MMIO_PHY_DATA); 2768c2ecf20Sopenharmony_ci} 2778c2ecf20Sopenharmony_ci 2788c2ecf20Sopenharmony_civoid b43_phy_write(struct b43_wldev *dev, u16 reg, u16 value) 2798c2ecf20Sopenharmony_ci{ 2808c2ecf20Sopenharmony_ci assert_mac_suspended(dev); 2818c2ecf20Sopenharmony_ci if (b43_bus_host_is_pci(dev->dev) && 2828c2ecf20Sopenharmony_ci ++dev->phy.writes_counter > B43_MAX_WRITES_IN_ROW) { 2838c2ecf20Sopenharmony_ci b43_read16(dev, B43_MMIO_PHY_VER); 2848c2ecf20Sopenharmony_ci dev->phy.writes_counter = 1; 2858c2ecf20Sopenharmony_ci } 2868c2ecf20Sopenharmony_ci 2878c2ecf20Sopenharmony_ci if (dev->phy.ops->phy_write) 2888c2ecf20Sopenharmony_ci return dev->phy.ops->phy_write(dev, reg, value); 2898c2ecf20Sopenharmony_ci 2908c2ecf20Sopenharmony_ci b43_write16f(dev, B43_MMIO_PHY_CONTROL, reg); 2918c2ecf20Sopenharmony_ci b43_write16(dev, B43_MMIO_PHY_DATA, value); 2928c2ecf20Sopenharmony_ci} 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_civoid b43_phy_copy(struct b43_wldev *dev, u16 destreg, u16 srcreg) 2958c2ecf20Sopenharmony_ci{ 2968c2ecf20Sopenharmony_ci b43_phy_write(dev, destreg, b43_phy_read(dev, srcreg)); 2978c2ecf20Sopenharmony_ci} 2988c2ecf20Sopenharmony_ci 2998c2ecf20Sopenharmony_civoid b43_phy_mask(struct b43_wldev *dev, u16 offset, u16 mask) 3008c2ecf20Sopenharmony_ci{ 3018c2ecf20Sopenharmony_ci if (dev->phy.ops->phy_maskset) { 3028c2ecf20Sopenharmony_ci assert_mac_suspended(dev); 3038c2ecf20Sopenharmony_ci dev->phy.ops->phy_maskset(dev, offset, mask, 0); 3048c2ecf20Sopenharmony_ci } else { 3058c2ecf20Sopenharmony_ci b43_phy_write(dev, offset, 3068c2ecf20Sopenharmony_ci b43_phy_read(dev, offset) & mask); 3078c2ecf20Sopenharmony_ci } 3088c2ecf20Sopenharmony_ci} 3098c2ecf20Sopenharmony_ci 3108c2ecf20Sopenharmony_civoid b43_phy_set(struct b43_wldev *dev, u16 offset, u16 set) 3118c2ecf20Sopenharmony_ci{ 3128c2ecf20Sopenharmony_ci if (dev->phy.ops->phy_maskset) { 3138c2ecf20Sopenharmony_ci assert_mac_suspended(dev); 3148c2ecf20Sopenharmony_ci dev->phy.ops->phy_maskset(dev, offset, 0xFFFF, set); 3158c2ecf20Sopenharmony_ci } else { 3168c2ecf20Sopenharmony_ci b43_phy_write(dev, offset, 3178c2ecf20Sopenharmony_ci b43_phy_read(dev, offset) | set); 3188c2ecf20Sopenharmony_ci } 3198c2ecf20Sopenharmony_ci} 3208c2ecf20Sopenharmony_ci 3218c2ecf20Sopenharmony_civoid b43_phy_maskset(struct b43_wldev *dev, u16 offset, u16 mask, u16 set) 3228c2ecf20Sopenharmony_ci{ 3238c2ecf20Sopenharmony_ci if (dev->phy.ops->phy_maskset) { 3248c2ecf20Sopenharmony_ci assert_mac_suspended(dev); 3258c2ecf20Sopenharmony_ci dev->phy.ops->phy_maskset(dev, offset, mask, set); 3268c2ecf20Sopenharmony_ci } else { 3278c2ecf20Sopenharmony_ci b43_phy_write(dev, offset, 3288c2ecf20Sopenharmony_ci (b43_phy_read(dev, offset) & mask) | set); 3298c2ecf20Sopenharmony_ci } 3308c2ecf20Sopenharmony_ci} 3318c2ecf20Sopenharmony_ci 3328c2ecf20Sopenharmony_civoid b43_phy_put_into_reset(struct b43_wldev *dev) 3338c2ecf20Sopenharmony_ci{ 3348c2ecf20Sopenharmony_ci u32 tmp; 3358c2ecf20Sopenharmony_ci 3368c2ecf20Sopenharmony_ci switch (dev->dev->bus_type) { 3378c2ecf20Sopenharmony_ci#ifdef CONFIG_B43_BCMA 3388c2ecf20Sopenharmony_ci case B43_BUS_BCMA: 3398c2ecf20Sopenharmony_ci tmp = bcma_aread32(dev->dev->bdev, BCMA_IOCTL); 3408c2ecf20Sopenharmony_ci tmp &= ~B43_BCMA_IOCTL_GMODE; 3418c2ecf20Sopenharmony_ci tmp |= B43_BCMA_IOCTL_PHY_RESET; 3428c2ecf20Sopenharmony_ci tmp |= BCMA_IOCTL_FGC; 3438c2ecf20Sopenharmony_ci bcma_awrite32(dev->dev->bdev, BCMA_IOCTL, tmp); 3448c2ecf20Sopenharmony_ci udelay(1); 3458c2ecf20Sopenharmony_ci 3468c2ecf20Sopenharmony_ci tmp = bcma_aread32(dev->dev->bdev, BCMA_IOCTL); 3478c2ecf20Sopenharmony_ci tmp &= ~BCMA_IOCTL_FGC; 3488c2ecf20Sopenharmony_ci bcma_awrite32(dev->dev->bdev, BCMA_IOCTL, tmp); 3498c2ecf20Sopenharmony_ci udelay(1); 3508c2ecf20Sopenharmony_ci break; 3518c2ecf20Sopenharmony_ci#endif 3528c2ecf20Sopenharmony_ci#ifdef CONFIG_B43_SSB 3538c2ecf20Sopenharmony_ci case B43_BUS_SSB: 3548c2ecf20Sopenharmony_ci tmp = ssb_read32(dev->dev->sdev, SSB_TMSLOW); 3558c2ecf20Sopenharmony_ci tmp &= ~B43_TMSLOW_GMODE; 3568c2ecf20Sopenharmony_ci tmp |= B43_TMSLOW_PHYRESET; 3578c2ecf20Sopenharmony_ci tmp |= SSB_TMSLOW_FGC; 3588c2ecf20Sopenharmony_ci ssb_write32(dev->dev->sdev, SSB_TMSLOW, tmp); 3598c2ecf20Sopenharmony_ci usleep_range(1000, 2000); 3608c2ecf20Sopenharmony_ci 3618c2ecf20Sopenharmony_ci tmp = ssb_read32(dev->dev->sdev, SSB_TMSLOW); 3628c2ecf20Sopenharmony_ci tmp &= ~SSB_TMSLOW_FGC; 3638c2ecf20Sopenharmony_ci ssb_write32(dev->dev->sdev, SSB_TMSLOW, tmp); 3648c2ecf20Sopenharmony_ci usleep_range(1000, 2000); 3658c2ecf20Sopenharmony_ci 3668c2ecf20Sopenharmony_ci break; 3678c2ecf20Sopenharmony_ci#endif 3688c2ecf20Sopenharmony_ci } 3698c2ecf20Sopenharmony_ci} 3708c2ecf20Sopenharmony_ci 3718c2ecf20Sopenharmony_civoid b43_phy_take_out_of_reset(struct b43_wldev *dev) 3728c2ecf20Sopenharmony_ci{ 3738c2ecf20Sopenharmony_ci u32 tmp; 3748c2ecf20Sopenharmony_ci 3758c2ecf20Sopenharmony_ci switch (dev->dev->bus_type) { 3768c2ecf20Sopenharmony_ci#ifdef CONFIG_B43_BCMA 3778c2ecf20Sopenharmony_ci case B43_BUS_BCMA: 3788c2ecf20Sopenharmony_ci /* Unset reset bit (with forcing clock) */ 3798c2ecf20Sopenharmony_ci tmp = bcma_aread32(dev->dev->bdev, BCMA_IOCTL); 3808c2ecf20Sopenharmony_ci tmp &= ~B43_BCMA_IOCTL_PHY_RESET; 3818c2ecf20Sopenharmony_ci tmp &= ~B43_BCMA_IOCTL_PHY_CLKEN; 3828c2ecf20Sopenharmony_ci tmp |= BCMA_IOCTL_FGC; 3838c2ecf20Sopenharmony_ci bcma_awrite32(dev->dev->bdev, BCMA_IOCTL, tmp); 3848c2ecf20Sopenharmony_ci udelay(1); 3858c2ecf20Sopenharmony_ci 3868c2ecf20Sopenharmony_ci /* Do not force clock anymore */ 3878c2ecf20Sopenharmony_ci tmp = bcma_aread32(dev->dev->bdev, BCMA_IOCTL); 3888c2ecf20Sopenharmony_ci tmp &= ~BCMA_IOCTL_FGC; 3898c2ecf20Sopenharmony_ci tmp |= B43_BCMA_IOCTL_PHY_CLKEN; 3908c2ecf20Sopenharmony_ci bcma_awrite32(dev->dev->bdev, BCMA_IOCTL, tmp); 3918c2ecf20Sopenharmony_ci udelay(1); 3928c2ecf20Sopenharmony_ci break; 3938c2ecf20Sopenharmony_ci#endif 3948c2ecf20Sopenharmony_ci#ifdef CONFIG_B43_SSB 3958c2ecf20Sopenharmony_ci case B43_BUS_SSB: 3968c2ecf20Sopenharmony_ci /* Unset reset bit (with forcing clock) */ 3978c2ecf20Sopenharmony_ci tmp = ssb_read32(dev->dev->sdev, SSB_TMSLOW); 3988c2ecf20Sopenharmony_ci tmp &= ~B43_TMSLOW_PHYRESET; 3998c2ecf20Sopenharmony_ci tmp &= ~B43_TMSLOW_PHYCLKEN; 4008c2ecf20Sopenharmony_ci tmp |= SSB_TMSLOW_FGC; 4018c2ecf20Sopenharmony_ci ssb_write32(dev->dev->sdev, SSB_TMSLOW, tmp); 4028c2ecf20Sopenharmony_ci ssb_read32(dev->dev->sdev, SSB_TMSLOW); /* flush */ 4038c2ecf20Sopenharmony_ci usleep_range(1000, 2000); 4048c2ecf20Sopenharmony_ci 4058c2ecf20Sopenharmony_ci tmp = ssb_read32(dev->dev->sdev, SSB_TMSLOW); 4068c2ecf20Sopenharmony_ci tmp &= ~SSB_TMSLOW_FGC; 4078c2ecf20Sopenharmony_ci tmp |= B43_TMSLOW_PHYCLKEN; 4088c2ecf20Sopenharmony_ci ssb_write32(dev->dev->sdev, SSB_TMSLOW, tmp); 4098c2ecf20Sopenharmony_ci ssb_read32(dev->dev->sdev, SSB_TMSLOW); /* flush */ 4108c2ecf20Sopenharmony_ci usleep_range(1000, 2000); 4118c2ecf20Sopenharmony_ci break; 4128c2ecf20Sopenharmony_ci#endif 4138c2ecf20Sopenharmony_ci } 4148c2ecf20Sopenharmony_ci} 4158c2ecf20Sopenharmony_ci 4168c2ecf20Sopenharmony_ciint b43_switch_channel(struct b43_wldev *dev, unsigned int new_channel) 4178c2ecf20Sopenharmony_ci{ 4188c2ecf20Sopenharmony_ci struct b43_phy *phy = &(dev->phy); 4198c2ecf20Sopenharmony_ci u16 channelcookie, savedcookie; 4208c2ecf20Sopenharmony_ci int err; 4218c2ecf20Sopenharmony_ci 4228c2ecf20Sopenharmony_ci /* First we set the channel radio code to prevent the 4238c2ecf20Sopenharmony_ci * firmware from sending ghost packets. 4248c2ecf20Sopenharmony_ci */ 4258c2ecf20Sopenharmony_ci channelcookie = new_channel; 4268c2ecf20Sopenharmony_ci if (b43_current_band(dev->wl) == NL80211_BAND_5GHZ) 4278c2ecf20Sopenharmony_ci channelcookie |= B43_SHM_SH_CHAN_5GHZ; 4288c2ecf20Sopenharmony_ci /* FIXME: set 40Mhz flag if required */ 4298c2ecf20Sopenharmony_ci if (0) 4308c2ecf20Sopenharmony_ci channelcookie |= B43_SHM_SH_CHAN_40MHZ; 4318c2ecf20Sopenharmony_ci savedcookie = b43_shm_read16(dev, B43_SHM_SHARED, B43_SHM_SH_CHAN); 4328c2ecf20Sopenharmony_ci b43_shm_write16(dev, B43_SHM_SHARED, B43_SHM_SH_CHAN, channelcookie); 4338c2ecf20Sopenharmony_ci 4348c2ecf20Sopenharmony_ci /* Now try to switch the PHY hardware channel. */ 4358c2ecf20Sopenharmony_ci err = phy->ops->switch_channel(dev, new_channel); 4368c2ecf20Sopenharmony_ci if (err) 4378c2ecf20Sopenharmony_ci goto err_restore_cookie; 4388c2ecf20Sopenharmony_ci 4398c2ecf20Sopenharmony_ci /* Wait for the radio to tune to the channel and stabilize. */ 4408c2ecf20Sopenharmony_ci msleep(8); 4418c2ecf20Sopenharmony_ci 4428c2ecf20Sopenharmony_ci return 0; 4438c2ecf20Sopenharmony_ci 4448c2ecf20Sopenharmony_cierr_restore_cookie: 4458c2ecf20Sopenharmony_ci b43_shm_write16(dev, B43_SHM_SHARED, 4468c2ecf20Sopenharmony_ci B43_SHM_SH_CHAN, savedcookie); 4478c2ecf20Sopenharmony_ci 4488c2ecf20Sopenharmony_ci return err; 4498c2ecf20Sopenharmony_ci} 4508c2ecf20Sopenharmony_ci 4518c2ecf20Sopenharmony_civoid b43_software_rfkill(struct b43_wldev *dev, bool blocked) 4528c2ecf20Sopenharmony_ci{ 4538c2ecf20Sopenharmony_ci struct b43_phy *phy = &dev->phy; 4548c2ecf20Sopenharmony_ci 4558c2ecf20Sopenharmony_ci b43_mac_suspend(dev); 4568c2ecf20Sopenharmony_ci phy->ops->software_rfkill(dev, blocked); 4578c2ecf20Sopenharmony_ci phy->radio_on = !blocked; 4588c2ecf20Sopenharmony_ci b43_mac_enable(dev); 4598c2ecf20Sopenharmony_ci} 4608c2ecf20Sopenharmony_ci 4618c2ecf20Sopenharmony_ci/* 4628c2ecf20Sopenharmony_ci * b43_phy_txpower_adjust_work - TX power workqueue. 4638c2ecf20Sopenharmony_ci * 4648c2ecf20Sopenharmony_ci * Workqueue for updating the TX power parameters in hardware. 4658c2ecf20Sopenharmony_ci */ 4668c2ecf20Sopenharmony_civoid b43_phy_txpower_adjust_work(struct work_struct *work) 4678c2ecf20Sopenharmony_ci{ 4688c2ecf20Sopenharmony_ci struct b43_wl *wl = container_of(work, struct b43_wl, 4698c2ecf20Sopenharmony_ci txpower_adjust_work); 4708c2ecf20Sopenharmony_ci struct b43_wldev *dev; 4718c2ecf20Sopenharmony_ci 4728c2ecf20Sopenharmony_ci mutex_lock(&wl->mutex); 4738c2ecf20Sopenharmony_ci dev = wl->current_dev; 4748c2ecf20Sopenharmony_ci 4758c2ecf20Sopenharmony_ci if (likely(dev && (b43_status(dev) >= B43_STAT_STARTED))) 4768c2ecf20Sopenharmony_ci dev->phy.ops->adjust_txpower(dev); 4778c2ecf20Sopenharmony_ci 4788c2ecf20Sopenharmony_ci mutex_unlock(&wl->mutex); 4798c2ecf20Sopenharmony_ci} 4808c2ecf20Sopenharmony_ci 4818c2ecf20Sopenharmony_civoid b43_phy_txpower_check(struct b43_wldev *dev, unsigned int flags) 4828c2ecf20Sopenharmony_ci{ 4838c2ecf20Sopenharmony_ci struct b43_phy *phy = &dev->phy; 4848c2ecf20Sopenharmony_ci unsigned long now = jiffies; 4858c2ecf20Sopenharmony_ci enum b43_txpwr_result result; 4868c2ecf20Sopenharmony_ci 4878c2ecf20Sopenharmony_ci if (!(flags & B43_TXPWR_IGNORE_TIME)) { 4888c2ecf20Sopenharmony_ci /* Check if it's time for a TXpower check. */ 4898c2ecf20Sopenharmony_ci if (time_before(now, phy->next_txpwr_check_time)) 4908c2ecf20Sopenharmony_ci return; /* Not yet */ 4918c2ecf20Sopenharmony_ci } 4928c2ecf20Sopenharmony_ci /* The next check will be needed in two seconds, or later. */ 4938c2ecf20Sopenharmony_ci phy->next_txpwr_check_time = round_jiffies(now + (HZ * 2)); 4948c2ecf20Sopenharmony_ci 4958c2ecf20Sopenharmony_ci if ((dev->dev->board_vendor == SSB_BOARDVENDOR_BCM) && 4968c2ecf20Sopenharmony_ci (dev->dev->board_type == SSB_BOARD_BU4306)) 4978c2ecf20Sopenharmony_ci return; /* No software txpower adjustment needed */ 4988c2ecf20Sopenharmony_ci 4998c2ecf20Sopenharmony_ci result = phy->ops->recalc_txpower(dev, !!(flags & B43_TXPWR_IGNORE_TSSI)); 5008c2ecf20Sopenharmony_ci if (result == B43_TXPWR_RES_DONE) 5018c2ecf20Sopenharmony_ci return; /* We are done. */ 5028c2ecf20Sopenharmony_ci B43_WARN_ON(result != B43_TXPWR_RES_NEED_ADJUST); 5038c2ecf20Sopenharmony_ci B43_WARN_ON(phy->ops->adjust_txpower == NULL); 5048c2ecf20Sopenharmony_ci 5058c2ecf20Sopenharmony_ci /* We must adjust the transmission power in hardware. 5068c2ecf20Sopenharmony_ci * Schedule b43_phy_txpower_adjust_work(). */ 5078c2ecf20Sopenharmony_ci ieee80211_queue_work(dev->wl->hw, &dev->wl->txpower_adjust_work); 5088c2ecf20Sopenharmony_ci} 5098c2ecf20Sopenharmony_ci 5108c2ecf20Sopenharmony_ciint b43_phy_shm_tssi_read(struct b43_wldev *dev, u16 shm_offset) 5118c2ecf20Sopenharmony_ci{ 5128c2ecf20Sopenharmony_ci const bool is_ofdm = (shm_offset != B43_SHM_SH_TSSI_CCK); 5138c2ecf20Sopenharmony_ci unsigned int a, b, c, d; 5148c2ecf20Sopenharmony_ci unsigned int average; 5158c2ecf20Sopenharmony_ci u32 tmp; 5168c2ecf20Sopenharmony_ci 5178c2ecf20Sopenharmony_ci tmp = b43_shm_read32(dev, B43_SHM_SHARED, shm_offset); 5188c2ecf20Sopenharmony_ci a = tmp & 0xFF; 5198c2ecf20Sopenharmony_ci b = (tmp >> 8) & 0xFF; 5208c2ecf20Sopenharmony_ci c = (tmp >> 16) & 0xFF; 5218c2ecf20Sopenharmony_ci d = (tmp >> 24) & 0xFF; 5228c2ecf20Sopenharmony_ci if (a == 0 || a == B43_TSSI_MAX || 5238c2ecf20Sopenharmony_ci b == 0 || b == B43_TSSI_MAX || 5248c2ecf20Sopenharmony_ci c == 0 || c == B43_TSSI_MAX || 5258c2ecf20Sopenharmony_ci d == 0 || d == B43_TSSI_MAX) 5268c2ecf20Sopenharmony_ci return -ENOENT; 5278c2ecf20Sopenharmony_ci /* The values are OK. Clear them. */ 5288c2ecf20Sopenharmony_ci tmp = B43_TSSI_MAX | (B43_TSSI_MAX << 8) | 5298c2ecf20Sopenharmony_ci (B43_TSSI_MAX << 16) | (B43_TSSI_MAX << 24); 5308c2ecf20Sopenharmony_ci b43_shm_write32(dev, B43_SHM_SHARED, shm_offset, tmp); 5318c2ecf20Sopenharmony_ci 5328c2ecf20Sopenharmony_ci if (is_ofdm) { 5338c2ecf20Sopenharmony_ci a = (a + 32) & 0x3F; 5348c2ecf20Sopenharmony_ci b = (b + 32) & 0x3F; 5358c2ecf20Sopenharmony_ci c = (c + 32) & 0x3F; 5368c2ecf20Sopenharmony_ci d = (d + 32) & 0x3F; 5378c2ecf20Sopenharmony_ci } 5388c2ecf20Sopenharmony_ci 5398c2ecf20Sopenharmony_ci /* Get the average of the values with 0.5 added to each value. */ 5408c2ecf20Sopenharmony_ci average = (a + b + c + d + 2) / 4; 5418c2ecf20Sopenharmony_ci if (is_ofdm) { 5428c2ecf20Sopenharmony_ci /* Adjust for CCK-boost */ 5438c2ecf20Sopenharmony_ci if (b43_shm_read16(dev, B43_SHM_SHARED, B43_SHM_SH_HOSTF1) 5448c2ecf20Sopenharmony_ci & B43_HF_CCKBOOST) 5458c2ecf20Sopenharmony_ci average = (average >= 13) ? (average - 13) : 0; 5468c2ecf20Sopenharmony_ci } 5478c2ecf20Sopenharmony_ci 5488c2ecf20Sopenharmony_ci return average; 5498c2ecf20Sopenharmony_ci} 5508c2ecf20Sopenharmony_ci 5518c2ecf20Sopenharmony_civoid b43_phyop_switch_analog_generic(struct b43_wldev *dev, bool on) 5528c2ecf20Sopenharmony_ci{ 5538c2ecf20Sopenharmony_ci b43_write16(dev, B43_MMIO_PHY0, on ? 0 : 0xF4); 5548c2ecf20Sopenharmony_ci} 5558c2ecf20Sopenharmony_ci 5568c2ecf20Sopenharmony_ci 5578c2ecf20Sopenharmony_cibool b43_is_40mhz(struct b43_wldev *dev) 5588c2ecf20Sopenharmony_ci{ 5598c2ecf20Sopenharmony_ci return dev->phy.chandef->width == NL80211_CHAN_WIDTH_40; 5608c2ecf20Sopenharmony_ci} 5618c2ecf20Sopenharmony_ci 5628c2ecf20Sopenharmony_ci/* https://bcm-v4.sipsolutions.net/802.11/PHY/N/BmacPhyClkFgc */ 5638c2ecf20Sopenharmony_civoid b43_phy_force_clock(struct b43_wldev *dev, bool force) 5648c2ecf20Sopenharmony_ci{ 5658c2ecf20Sopenharmony_ci u32 tmp; 5668c2ecf20Sopenharmony_ci 5678c2ecf20Sopenharmony_ci WARN_ON(dev->phy.type != B43_PHYTYPE_N && 5688c2ecf20Sopenharmony_ci dev->phy.type != B43_PHYTYPE_HT && 5698c2ecf20Sopenharmony_ci dev->phy.type != B43_PHYTYPE_AC); 5708c2ecf20Sopenharmony_ci 5718c2ecf20Sopenharmony_ci switch (dev->dev->bus_type) { 5728c2ecf20Sopenharmony_ci#ifdef CONFIG_B43_BCMA 5738c2ecf20Sopenharmony_ci case B43_BUS_BCMA: 5748c2ecf20Sopenharmony_ci tmp = bcma_aread32(dev->dev->bdev, BCMA_IOCTL); 5758c2ecf20Sopenharmony_ci if (force) 5768c2ecf20Sopenharmony_ci tmp |= BCMA_IOCTL_FGC; 5778c2ecf20Sopenharmony_ci else 5788c2ecf20Sopenharmony_ci tmp &= ~BCMA_IOCTL_FGC; 5798c2ecf20Sopenharmony_ci bcma_awrite32(dev->dev->bdev, BCMA_IOCTL, tmp); 5808c2ecf20Sopenharmony_ci break; 5818c2ecf20Sopenharmony_ci#endif 5828c2ecf20Sopenharmony_ci#ifdef CONFIG_B43_SSB 5838c2ecf20Sopenharmony_ci case B43_BUS_SSB: 5848c2ecf20Sopenharmony_ci tmp = ssb_read32(dev->dev->sdev, SSB_TMSLOW); 5858c2ecf20Sopenharmony_ci if (force) 5868c2ecf20Sopenharmony_ci tmp |= SSB_TMSLOW_FGC; 5878c2ecf20Sopenharmony_ci else 5888c2ecf20Sopenharmony_ci tmp &= ~SSB_TMSLOW_FGC; 5898c2ecf20Sopenharmony_ci ssb_write32(dev->dev->sdev, SSB_TMSLOW, tmp); 5908c2ecf20Sopenharmony_ci break; 5918c2ecf20Sopenharmony_ci#endif 5928c2ecf20Sopenharmony_ci } 5938c2ecf20Sopenharmony_ci} 594