162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci 462306a36Sopenharmony_ci Broadcom B43 wireless driver 562306a36Sopenharmony_ci Common PHY routines 662306a36Sopenharmony_ci 762306a36Sopenharmony_ci Copyright (c) 2005 Martin Langer <martin-langer@gmx.de>, 862306a36Sopenharmony_ci Copyright (c) 2005-2007 Stefano Brivio <stefano.brivio@polimi.it> 962306a36Sopenharmony_ci Copyright (c) 2005-2008 Michael Buesch <m@bues.ch> 1062306a36Sopenharmony_ci Copyright (c) 2005, 2006 Danny van Dyk <kugelfang@gentoo.org> 1162306a36Sopenharmony_ci Copyright (c) 2005, 2006 Andreas Jaggi <andreas.jaggi@waterwave.ch> 1262306a36Sopenharmony_ci 1362306a36Sopenharmony_ci 1462306a36Sopenharmony_ci*/ 1562306a36Sopenharmony_ci 1662306a36Sopenharmony_ci#include "phy_common.h" 1762306a36Sopenharmony_ci#include "phy_g.h" 1862306a36Sopenharmony_ci#include "phy_a.h" 1962306a36Sopenharmony_ci#include "phy_n.h" 2062306a36Sopenharmony_ci#include "phy_lp.h" 2162306a36Sopenharmony_ci#include "phy_ht.h" 2262306a36Sopenharmony_ci#include "phy_lcn.h" 2362306a36Sopenharmony_ci#include "phy_ac.h" 2462306a36Sopenharmony_ci#include "b43.h" 2562306a36Sopenharmony_ci#include "main.h" 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_ciint b43_phy_allocate(struct b43_wldev *dev) 2962306a36Sopenharmony_ci{ 3062306a36Sopenharmony_ci struct b43_phy *phy = &(dev->phy); 3162306a36Sopenharmony_ci int err; 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ci phy->ops = NULL; 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_ci switch (phy->type) { 3662306a36Sopenharmony_ci case B43_PHYTYPE_G: 3762306a36Sopenharmony_ci#ifdef CONFIG_B43_PHY_G 3862306a36Sopenharmony_ci phy->ops = &b43_phyops_g; 3962306a36Sopenharmony_ci#endif 4062306a36Sopenharmony_ci break; 4162306a36Sopenharmony_ci case B43_PHYTYPE_N: 4262306a36Sopenharmony_ci#ifdef CONFIG_B43_PHY_N 4362306a36Sopenharmony_ci phy->ops = &b43_phyops_n; 4462306a36Sopenharmony_ci#endif 4562306a36Sopenharmony_ci break; 4662306a36Sopenharmony_ci case B43_PHYTYPE_LP: 4762306a36Sopenharmony_ci#ifdef CONFIG_B43_PHY_LP 4862306a36Sopenharmony_ci phy->ops = &b43_phyops_lp; 4962306a36Sopenharmony_ci#endif 5062306a36Sopenharmony_ci break; 5162306a36Sopenharmony_ci case B43_PHYTYPE_HT: 5262306a36Sopenharmony_ci#ifdef CONFIG_B43_PHY_HT 5362306a36Sopenharmony_ci phy->ops = &b43_phyops_ht; 5462306a36Sopenharmony_ci#endif 5562306a36Sopenharmony_ci break; 5662306a36Sopenharmony_ci case B43_PHYTYPE_LCN: 5762306a36Sopenharmony_ci#ifdef CONFIG_B43_PHY_LCN 5862306a36Sopenharmony_ci phy->ops = &b43_phyops_lcn; 5962306a36Sopenharmony_ci#endif 6062306a36Sopenharmony_ci break; 6162306a36Sopenharmony_ci case B43_PHYTYPE_AC: 6262306a36Sopenharmony_ci#ifdef CONFIG_B43_PHY_AC 6362306a36Sopenharmony_ci phy->ops = &b43_phyops_ac; 6462306a36Sopenharmony_ci#endif 6562306a36Sopenharmony_ci break; 6662306a36Sopenharmony_ci } 6762306a36Sopenharmony_ci if (B43_WARN_ON(!phy->ops)) 6862306a36Sopenharmony_ci return -ENODEV; 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ci err = phy->ops->allocate(dev); 7162306a36Sopenharmony_ci if (err) 7262306a36Sopenharmony_ci phy->ops = NULL; 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_ci return err; 7562306a36Sopenharmony_ci} 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_civoid b43_phy_free(struct b43_wldev *dev) 7862306a36Sopenharmony_ci{ 7962306a36Sopenharmony_ci dev->phy.ops->free(dev); 8062306a36Sopenharmony_ci dev->phy.ops = NULL; 8162306a36Sopenharmony_ci} 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_ciint b43_phy_init(struct b43_wldev *dev) 8462306a36Sopenharmony_ci{ 8562306a36Sopenharmony_ci struct b43_phy *phy = &dev->phy; 8662306a36Sopenharmony_ci const struct b43_phy_operations *ops = phy->ops; 8762306a36Sopenharmony_ci int err; 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_ci /* During PHY init we need to use some channel. On the first init this 9062306a36Sopenharmony_ci * function is called *before* b43_op_config, so our pointer is NULL. 9162306a36Sopenharmony_ci */ 9262306a36Sopenharmony_ci if (!phy->chandef) { 9362306a36Sopenharmony_ci phy->chandef = &dev->wl->hw->conf.chandef; 9462306a36Sopenharmony_ci phy->channel = phy->chandef->chan->hw_value; 9562306a36Sopenharmony_ci } 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_ci phy->ops->switch_analog(dev, true); 9862306a36Sopenharmony_ci b43_software_rfkill(dev, false); 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_ci err = ops->init(dev); 10162306a36Sopenharmony_ci if (err) { 10262306a36Sopenharmony_ci b43err(dev->wl, "PHY init failed\n"); 10362306a36Sopenharmony_ci goto err_block_rf; 10462306a36Sopenharmony_ci } 10562306a36Sopenharmony_ci phy->do_full_init = false; 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_ci err = b43_switch_channel(dev, phy->channel); 10862306a36Sopenharmony_ci if (err) { 10962306a36Sopenharmony_ci b43err(dev->wl, "PHY init: Channel switch to default failed\n"); 11062306a36Sopenharmony_ci goto err_phy_exit; 11162306a36Sopenharmony_ci } 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci return 0; 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_cierr_phy_exit: 11662306a36Sopenharmony_ci phy->do_full_init = true; 11762306a36Sopenharmony_ci if (ops->exit) 11862306a36Sopenharmony_ci ops->exit(dev); 11962306a36Sopenharmony_cierr_block_rf: 12062306a36Sopenharmony_ci b43_software_rfkill(dev, true); 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_ci return err; 12362306a36Sopenharmony_ci} 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_civoid b43_phy_exit(struct b43_wldev *dev) 12662306a36Sopenharmony_ci{ 12762306a36Sopenharmony_ci const struct b43_phy_operations *ops = dev->phy.ops; 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci b43_software_rfkill(dev, true); 13062306a36Sopenharmony_ci dev->phy.do_full_init = true; 13162306a36Sopenharmony_ci if (ops->exit) 13262306a36Sopenharmony_ci ops->exit(dev); 13362306a36Sopenharmony_ci} 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_cibool b43_has_hardware_pctl(struct b43_wldev *dev) 13662306a36Sopenharmony_ci{ 13762306a36Sopenharmony_ci if (!dev->phy.hardware_power_control) 13862306a36Sopenharmony_ci return false; 13962306a36Sopenharmony_ci if (!dev->phy.ops->supports_hwpctl) 14062306a36Sopenharmony_ci return false; 14162306a36Sopenharmony_ci return dev->phy.ops->supports_hwpctl(dev); 14262306a36Sopenharmony_ci} 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_civoid b43_radio_lock(struct b43_wldev *dev) 14562306a36Sopenharmony_ci{ 14662306a36Sopenharmony_ci u32 macctl; 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ci#if B43_DEBUG 14962306a36Sopenharmony_ci B43_WARN_ON(dev->phy.radio_locked); 15062306a36Sopenharmony_ci dev->phy.radio_locked = true; 15162306a36Sopenharmony_ci#endif 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_ci macctl = b43_read32(dev, B43_MMIO_MACCTL); 15462306a36Sopenharmony_ci macctl |= B43_MACCTL_RADIOLOCK; 15562306a36Sopenharmony_ci b43_write32(dev, B43_MMIO_MACCTL, macctl); 15662306a36Sopenharmony_ci /* Commit the write and wait for the firmware 15762306a36Sopenharmony_ci * to finish any radio register access. */ 15862306a36Sopenharmony_ci b43_read32(dev, B43_MMIO_MACCTL); 15962306a36Sopenharmony_ci udelay(10); 16062306a36Sopenharmony_ci} 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_civoid b43_radio_unlock(struct b43_wldev *dev) 16362306a36Sopenharmony_ci{ 16462306a36Sopenharmony_ci u32 macctl; 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_ci#if B43_DEBUG 16762306a36Sopenharmony_ci B43_WARN_ON(!dev->phy.radio_locked); 16862306a36Sopenharmony_ci dev->phy.radio_locked = false; 16962306a36Sopenharmony_ci#endif 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_ci /* Commit any write */ 17262306a36Sopenharmony_ci b43_read16(dev, B43_MMIO_PHY_VER); 17362306a36Sopenharmony_ci /* unlock */ 17462306a36Sopenharmony_ci macctl = b43_read32(dev, B43_MMIO_MACCTL); 17562306a36Sopenharmony_ci macctl &= ~B43_MACCTL_RADIOLOCK; 17662306a36Sopenharmony_ci b43_write32(dev, B43_MMIO_MACCTL, macctl); 17762306a36Sopenharmony_ci} 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_civoid b43_phy_lock(struct b43_wldev *dev) 18062306a36Sopenharmony_ci{ 18162306a36Sopenharmony_ci#if B43_DEBUG 18262306a36Sopenharmony_ci B43_WARN_ON(dev->phy.phy_locked); 18362306a36Sopenharmony_ci dev->phy.phy_locked = true; 18462306a36Sopenharmony_ci#endif 18562306a36Sopenharmony_ci B43_WARN_ON(dev->dev->core_rev < 3); 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_ci if (!b43_is_mode(dev->wl, NL80211_IFTYPE_AP)) 18862306a36Sopenharmony_ci b43_power_saving_ctl_bits(dev, B43_PS_AWAKE); 18962306a36Sopenharmony_ci} 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_civoid b43_phy_unlock(struct b43_wldev *dev) 19262306a36Sopenharmony_ci{ 19362306a36Sopenharmony_ci#if B43_DEBUG 19462306a36Sopenharmony_ci B43_WARN_ON(!dev->phy.phy_locked); 19562306a36Sopenharmony_ci dev->phy.phy_locked = false; 19662306a36Sopenharmony_ci#endif 19762306a36Sopenharmony_ci B43_WARN_ON(dev->dev->core_rev < 3); 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_ci if (!b43_is_mode(dev->wl, NL80211_IFTYPE_AP)) 20062306a36Sopenharmony_ci b43_power_saving_ctl_bits(dev, 0); 20162306a36Sopenharmony_ci} 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_cistatic inline void assert_mac_suspended(struct b43_wldev *dev) 20462306a36Sopenharmony_ci{ 20562306a36Sopenharmony_ci if (!B43_DEBUG) 20662306a36Sopenharmony_ci return; 20762306a36Sopenharmony_ci if ((b43_status(dev) >= B43_STAT_INITIALIZED) && 20862306a36Sopenharmony_ci (dev->mac_suspended <= 0)) { 20962306a36Sopenharmony_ci b43dbg(dev->wl, "PHY/RADIO register access with " 21062306a36Sopenharmony_ci "enabled MAC.\n"); 21162306a36Sopenharmony_ci dump_stack(); 21262306a36Sopenharmony_ci } 21362306a36Sopenharmony_ci} 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_ciu16 b43_radio_read(struct b43_wldev *dev, u16 reg) 21662306a36Sopenharmony_ci{ 21762306a36Sopenharmony_ci assert_mac_suspended(dev); 21862306a36Sopenharmony_ci dev->phy.writes_counter = 0; 21962306a36Sopenharmony_ci return dev->phy.ops->radio_read(dev, reg); 22062306a36Sopenharmony_ci} 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_civoid b43_radio_write(struct b43_wldev *dev, u16 reg, u16 value) 22362306a36Sopenharmony_ci{ 22462306a36Sopenharmony_ci assert_mac_suspended(dev); 22562306a36Sopenharmony_ci if (b43_bus_host_is_pci(dev->dev) && 22662306a36Sopenharmony_ci ++dev->phy.writes_counter > B43_MAX_WRITES_IN_ROW) { 22762306a36Sopenharmony_ci b43_read32(dev, B43_MMIO_MACCTL); 22862306a36Sopenharmony_ci dev->phy.writes_counter = 1; 22962306a36Sopenharmony_ci } 23062306a36Sopenharmony_ci dev->phy.ops->radio_write(dev, reg, value); 23162306a36Sopenharmony_ci} 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_civoid b43_radio_mask(struct b43_wldev *dev, u16 offset, u16 mask) 23462306a36Sopenharmony_ci{ 23562306a36Sopenharmony_ci b43_radio_write16(dev, offset, 23662306a36Sopenharmony_ci b43_radio_read16(dev, offset) & mask); 23762306a36Sopenharmony_ci} 23862306a36Sopenharmony_ci 23962306a36Sopenharmony_civoid b43_radio_set(struct b43_wldev *dev, u16 offset, u16 set) 24062306a36Sopenharmony_ci{ 24162306a36Sopenharmony_ci b43_radio_write16(dev, offset, 24262306a36Sopenharmony_ci b43_radio_read16(dev, offset) | set); 24362306a36Sopenharmony_ci} 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_civoid b43_radio_maskset(struct b43_wldev *dev, u16 offset, u16 mask, u16 set) 24662306a36Sopenharmony_ci{ 24762306a36Sopenharmony_ci b43_radio_write16(dev, offset, 24862306a36Sopenharmony_ci (b43_radio_read16(dev, offset) & mask) | set); 24962306a36Sopenharmony_ci} 25062306a36Sopenharmony_ci 25162306a36Sopenharmony_cibool b43_radio_wait_value(struct b43_wldev *dev, u16 offset, u16 mask, 25262306a36Sopenharmony_ci u16 value, int delay, int timeout) 25362306a36Sopenharmony_ci{ 25462306a36Sopenharmony_ci u16 val; 25562306a36Sopenharmony_ci int i; 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_ci for (i = 0; i < timeout; i += delay) { 25862306a36Sopenharmony_ci val = b43_radio_read(dev, offset); 25962306a36Sopenharmony_ci if ((val & mask) == value) 26062306a36Sopenharmony_ci return true; 26162306a36Sopenharmony_ci udelay(delay); 26262306a36Sopenharmony_ci } 26362306a36Sopenharmony_ci return false; 26462306a36Sopenharmony_ci} 26562306a36Sopenharmony_ci 26662306a36Sopenharmony_ciu16 b43_phy_read(struct b43_wldev *dev, u16 reg) 26762306a36Sopenharmony_ci{ 26862306a36Sopenharmony_ci assert_mac_suspended(dev); 26962306a36Sopenharmony_ci dev->phy.writes_counter = 0; 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_ci if (dev->phy.ops->phy_read) 27262306a36Sopenharmony_ci return dev->phy.ops->phy_read(dev, reg); 27362306a36Sopenharmony_ci 27462306a36Sopenharmony_ci b43_write16f(dev, B43_MMIO_PHY_CONTROL, reg); 27562306a36Sopenharmony_ci return b43_read16(dev, B43_MMIO_PHY_DATA); 27662306a36Sopenharmony_ci} 27762306a36Sopenharmony_ci 27862306a36Sopenharmony_civoid b43_phy_write(struct b43_wldev *dev, u16 reg, u16 value) 27962306a36Sopenharmony_ci{ 28062306a36Sopenharmony_ci assert_mac_suspended(dev); 28162306a36Sopenharmony_ci if (b43_bus_host_is_pci(dev->dev) && 28262306a36Sopenharmony_ci ++dev->phy.writes_counter > B43_MAX_WRITES_IN_ROW) { 28362306a36Sopenharmony_ci b43_read16(dev, B43_MMIO_PHY_VER); 28462306a36Sopenharmony_ci dev->phy.writes_counter = 1; 28562306a36Sopenharmony_ci } 28662306a36Sopenharmony_ci 28762306a36Sopenharmony_ci if (dev->phy.ops->phy_write) 28862306a36Sopenharmony_ci return dev->phy.ops->phy_write(dev, reg, value); 28962306a36Sopenharmony_ci 29062306a36Sopenharmony_ci b43_write16f(dev, B43_MMIO_PHY_CONTROL, reg); 29162306a36Sopenharmony_ci b43_write16(dev, B43_MMIO_PHY_DATA, value); 29262306a36Sopenharmony_ci} 29362306a36Sopenharmony_ci 29462306a36Sopenharmony_civoid b43_phy_copy(struct b43_wldev *dev, u16 destreg, u16 srcreg) 29562306a36Sopenharmony_ci{ 29662306a36Sopenharmony_ci b43_phy_write(dev, destreg, b43_phy_read(dev, srcreg)); 29762306a36Sopenharmony_ci} 29862306a36Sopenharmony_ci 29962306a36Sopenharmony_civoid b43_phy_mask(struct b43_wldev *dev, u16 offset, u16 mask) 30062306a36Sopenharmony_ci{ 30162306a36Sopenharmony_ci if (dev->phy.ops->phy_maskset) { 30262306a36Sopenharmony_ci assert_mac_suspended(dev); 30362306a36Sopenharmony_ci dev->phy.ops->phy_maskset(dev, offset, mask, 0); 30462306a36Sopenharmony_ci } else { 30562306a36Sopenharmony_ci b43_phy_write(dev, offset, 30662306a36Sopenharmony_ci b43_phy_read(dev, offset) & mask); 30762306a36Sopenharmony_ci } 30862306a36Sopenharmony_ci} 30962306a36Sopenharmony_ci 31062306a36Sopenharmony_civoid b43_phy_set(struct b43_wldev *dev, u16 offset, u16 set) 31162306a36Sopenharmony_ci{ 31262306a36Sopenharmony_ci if (dev->phy.ops->phy_maskset) { 31362306a36Sopenharmony_ci assert_mac_suspended(dev); 31462306a36Sopenharmony_ci dev->phy.ops->phy_maskset(dev, offset, 0xFFFF, set); 31562306a36Sopenharmony_ci } else { 31662306a36Sopenharmony_ci b43_phy_write(dev, offset, 31762306a36Sopenharmony_ci b43_phy_read(dev, offset) | set); 31862306a36Sopenharmony_ci } 31962306a36Sopenharmony_ci} 32062306a36Sopenharmony_ci 32162306a36Sopenharmony_civoid b43_phy_maskset(struct b43_wldev *dev, u16 offset, u16 mask, u16 set) 32262306a36Sopenharmony_ci{ 32362306a36Sopenharmony_ci if (dev->phy.ops->phy_maskset) { 32462306a36Sopenharmony_ci assert_mac_suspended(dev); 32562306a36Sopenharmony_ci dev->phy.ops->phy_maskset(dev, offset, mask, set); 32662306a36Sopenharmony_ci } else { 32762306a36Sopenharmony_ci b43_phy_write(dev, offset, 32862306a36Sopenharmony_ci (b43_phy_read(dev, offset) & mask) | set); 32962306a36Sopenharmony_ci } 33062306a36Sopenharmony_ci} 33162306a36Sopenharmony_ci 33262306a36Sopenharmony_civoid b43_phy_put_into_reset(struct b43_wldev *dev) 33362306a36Sopenharmony_ci{ 33462306a36Sopenharmony_ci u32 tmp; 33562306a36Sopenharmony_ci 33662306a36Sopenharmony_ci switch (dev->dev->bus_type) { 33762306a36Sopenharmony_ci#ifdef CONFIG_B43_BCMA 33862306a36Sopenharmony_ci case B43_BUS_BCMA: 33962306a36Sopenharmony_ci tmp = bcma_aread32(dev->dev->bdev, BCMA_IOCTL); 34062306a36Sopenharmony_ci tmp &= ~B43_BCMA_IOCTL_GMODE; 34162306a36Sopenharmony_ci tmp |= B43_BCMA_IOCTL_PHY_RESET; 34262306a36Sopenharmony_ci tmp |= BCMA_IOCTL_FGC; 34362306a36Sopenharmony_ci bcma_awrite32(dev->dev->bdev, BCMA_IOCTL, tmp); 34462306a36Sopenharmony_ci udelay(1); 34562306a36Sopenharmony_ci 34662306a36Sopenharmony_ci tmp = bcma_aread32(dev->dev->bdev, BCMA_IOCTL); 34762306a36Sopenharmony_ci tmp &= ~BCMA_IOCTL_FGC; 34862306a36Sopenharmony_ci bcma_awrite32(dev->dev->bdev, BCMA_IOCTL, tmp); 34962306a36Sopenharmony_ci udelay(1); 35062306a36Sopenharmony_ci break; 35162306a36Sopenharmony_ci#endif 35262306a36Sopenharmony_ci#ifdef CONFIG_B43_SSB 35362306a36Sopenharmony_ci case B43_BUS_SSB: 35462306a36Sopenharmony_ci tmp = ssb_read32(dev->dev->sdev, SSB_TMSLOW); 35562306a36Sopenharmony_ci tmp &= ~B43_TMSLOW_GMODE; 35662306a36Sopenharmony_ci tmp |= B43_TMSLOW_PHYRESET; 35762306a36Sopenharmony_ci tmp |= SSB_TMSLOW_FGC; 35862306a36Sopenharmony_ci ssb_write32(dev->dev->sdev, SSB_TMSLOW, tmp); 35962306a36Sopenharmony_ci usleep_range(1000, 2000); 36062306a36Sopenharmony_ci 36162306a36Sopenharmony_ci tmp = ssb_read32(dev->dev->sdev, SSB_TMSLOW); 36262306a36Sopenharmony_ci tmp &= ~SSB_TMSLOW_FGC; 36362306a36Sopenharmony_ci ssb_write32(dev->dev->sdev, SSB_TMSLOW, tmp); 36462306a36Sopenharmony_ci usleep_range(1000, 2000); 36562306a36Sopenharmony_ci 36662306a36Sopenharmony_ci break; 36762306a36Sopenharmony_ci#endif 36862306a36Sopenharmony_ci } 36962306a36Sopenharmony_ci} 37062306a36Sopenharmony_ci 37162306a36Sopenharmony_civoid b43_phy_take_out_of_reset(struct b43_wldev *dev) 37262306a36Sopenharmony_ci{ 37362306a36Sopenharmony_ci u32 tmp; 37462306a36Sopenharmony_ci 37562306a36Sopenharmony_ci switch (dev->dev->bus_type) { 37662306a36Sopenharmony_ci#ifdef CONFIG_B43_BCMA 37762306a36Sopenharmony_ci case B43_BUS_BCMA: 37862306a36Sopenharmony_ci /* Unset reset bit (with forcing clock) */ 37962306a36Sopenharmony_ci tmp = bcma_aread32(dev->dev->bdev, BCMA_IOCTL); 38062306a36Sopenharmony_ci tmp &= ~B43_BCMA_IOCTL_PHY_RESET; 38162306a36Sopenharmony_ci tmp &= ~B43_BCMA_IOCTL_PHY_CLKEN; 38262306a36Sopenharmony_ci tmp |= BCMA_IOCTL_FGC; 38362306a36Sopenharmony_ci bcma_awrite32(dev->dev->bdev, BCMA_IOCTL, tmp); 38462306a36Sopenharmony_ci udelay(1); 38562306a36Sopenharmony_ci 38662306a36Sopenharmony_ci /* Do not force clock anymore */ 38762306a36Sopenharmony_ci tmp = bcma_aread32(dev->dev->bdev, BCMA_IOCTL); 38862306a36Sopenharmony_ci tmp &= ~BCMA_IOCTL_FGC; 38962306a36Sopenharmony_ci tmp |= B43_BCMA_IOCTL_PHY_CLKEN; 39062306a36Sopenharmony_ci bcma_awrite32(dev->dev->bdev, BCMA_IOCTL, tmp); 39162306a36Sopenharmony_ci udelay(1); 39262306a36Sopenharmony_ci break; 39362306a36Sopenharmony_ci#endif 39462306a36Sopenharmony_ci#ifdef CONFIG_B43_SSB 39562306a36Sopenharmony_ci case B43_BUS_SSB: 39662306a36Sopenharmony_ci /* Unset reset bit (with forcing clock) */ 39762306a36Sopenharmony_ci tmp = ssb_read32(dev->dev->sdev, SSB_TMSLOW); 39862306a36Sopenharmony_ci tmp &= ~B43_TMSLOW_PHYRESET; 39962306a36Sopenharmony_ci tmp &= ~B43_TMSLOW_PHYCLKEN; 40062306a36Sopenharmony_ci tmp |= SSB_TMSLOW_FGC; 40162306a36Sopenharmony_ci ssb_write32(dev->dev->sdev, SSB_TMSLOW, tmp); 40262306a36Sopenharmony_ci ssb_read32(dev->dev->sdev, SSB_TMSLOW); /* flush */ 40362306a36Sopenharmony_ci usleep_range(1000, 2000); 40462306a36Sopenharmony_ci 40562306a36Sopenharmony_ci tmp = ssb_read32(dev->dev->sdev, SSB_TMSLOW); 40662306a36Sopenharmony_ci tmp &= ~SSB_TMSLOW_FGC; 40762306a36Sopenharmony_ci tmp |= B43_TMSLOW_PHYCLKEN; 40862306a36Sopenharmony_ci ssb_write32(dev->dev->sdev, SSB_TMSLOW, tmp); 40962306a36Sopenharmony_ci ssb_read32(dev->dev->sdev, SSB_TMSLOW); /* flush */ 41062306a36Sopenharmony_ci usleep_range(1000, 2000); 41162306a36Sopenharmony_ci break; 41262306a36Sopenharmony_ci#endif 41362306a36Sopenharmony_ci } 41462306a36Sopenharmony_ci} 41562306a36Sopenharmony_ci 41662306a36Sopenharmony_ciint b43_switch_channel(struct b43_wldev *dev, unsigned int new_channel) 41762306a36Sopenharmony_ci{ 41862306a36Sopenharmony_ci struct b43_phy *phy = &(dev->phy); 41962306a36Sopenharmony_ci u16 channelcookie, savedcookie; 42062306a36Sopenharmony_ci int err; 42162306a36Sopenharmony_ci 42262306a36Sopenharmony_ci /* First we set the channel radio code to prevent the 42362306a36Sopenharmony_ci * firmware from sending ghost packets. 42462306a36Sopenharmony_ci */ 42562306a36Sopenharmony_ci channelcookie = new_channel; 42662306a36Sopenharmony_ci if (b43_current_band(dev->wl) == NL80211_BAND_5GHZ) 42762306a36Sopenharmony_ci channelcookie |= B43_SHM_SH_CHAN_5GHZ; 42862306a36Sopenharmony_ci /* FIXME: set 40Mhz flag if required */ 42962306a36Sopenharmony_ci if (0) 43062306a36Sopenharmony_ci channelcookie |= B43_SHM_SH_CHAN_40MHZ; 43162306a36Sopenharmony_ci savedcookie = b43_shm_read16(dev, B43_SHM_SHARED, B43_SHM_SH_CHAN); 43262306a36Sopenharmony_ci b43_shm_write16(dev, B43_SHM_SHARED, B43_SHM_SH_CHAN, channelcookie); 43362306a36Sopenharmony_ci 43462306a36Sopenharmony_ci /* Now try to switch the PHY hardware channel. */ 43562306a36Sopenharmony_ci err = phy->ops->switch_channel(dev, new_channel); 43662306a36Sopenharmony_ci if (err) 43762306a36Sopenharmony_ci goto err_restore_cookie; 43862306a36Sopenharmony_ci 43962306a36Sopenharmony_ci /* Wait for the radio to tune to the channel and stabilize. */ 44062306a36Sopenharmony_ci msleep(8); 44162306a36Sopenharmony_ci 44262306a36Sopenharmony_ci return 0; 44362306a36Sopenharmony_ci 44462306a36Sopenharmony_cierr_restore_cookie: 44562306a36Sopenharmony_ci b43_shm_write16(dev, B43_SHM_SHARED, 44662306a36Sopenharmony_ci B43_SHM_SH_CHAN, savedcookie); 44762306a36Sopenharmony_ci 44862306a36Sopenharmony_ci return err; 44962306a36Sopenharmony_ci} 45062306a36Sopenharmony_ci 45162306a36Sopenharmony_civoid b43_software_rfkill(struct b43_wldev *dev, bool blocked) 45262306a36Sopenharmony_ci{ 45362306a36Sopenharmony_ci struct b43_phy *phy = &dev->phy; 45462306a36Sopenharmony_ci 45562306a36Sopenharmony_ci b43_mac_suspend(dev); 45662306a36Sopenharmony_ci phy->ops->software_rfkill(dev, blocked); 45762306a36Sopenharmony_ci phy->radio_on = !blocked; 45862306a36Sopenharmony_ci b43_mac_enable(dev); 45962306a36Sopenharmony_ci} 46062306a36Sopenharmony_ci 46162306a36Sopenharmony_ci/* 46262306a36Sopenharmony_ci * b43_phy_txpower_adjust_work - TX power workqueue. 46362306a36Sopenharmony_ci * 46462306a36Sopenharmony_ci * Workqueue for updating the TX power parameters in hardware. 46562306a36Sopenharmony_ci */ 46662306a36Sopenharmony_civoid b43_phy_txpower_adjust_work(struct work_struct *work) 46762306a36Sopenharmony_ci{ 46862306a36Sopenharmony_ci struct b43_wl *wl = container_of(work, struct b43_wl, 46962306a36Sopenharmony_ci txpower_adjust_work); 47062306a36Sopenharmony_ci struct b43_wldev *dev; 47162306a36Sopenharmony_ci 47262306a36Sopenharmony_ci mutex_lock(&wl->mutex); 47362306a36Sopenharmony_ci dev = wl->current_dev; 47462306a36Sopenharmony_ci 47562306a36Sopenharmony_ci if (likely(dev && (b43_status(dev) >= B43_STAT_STARTED))) 47662306a36Sopenharmony_ci dev->phy.ops->adjust_txpower(dev); 47762306a36Sopenharmony_ci 47862306a36Sopenharmony_ci mutex_unlock(&wl->mutex); 47962306a36Sopenharmony_ci} 48062306a36Sopenharmony_ci 48162306a36Sopenharmony_civoid b43_phy_txpower_check(struct b43_wldev *dev, unsigned int flags) 48262306a36Sopenharmony_ci{ 48362306a36Sopenharmony_ci struct b43_phy *phy = &dev->phy; 48462306a36Sopenharmony_ci unsigned long now = jiffies; 48562306a36Sopenharmony_ci enum b43_txpwr_result result; 48662306a36Sopenharmony_ci 48762306a36Sopenharmony_ci if (!(flags & B43_TXPWR_IGNORE_TIME)) { 48862306a36Sopenharmony_ci /* Check if it's time for a TXpower check. */ 48962306a36Sopenharmony_ci if (time_before(now, phy->next_txpwr_check_time)) 49062306a36Sopenharmony_ci return; /* Not yet */ 49162306a36Sopenharmony_ci } 49262306a36Sopenharmony_ci /* The next check will be needed in two seconds, or later. */ 49362306a36Sopenharmony_ci phy->next_txpwr_check_time = round_jiffies(now + (HZ * 2)); 49462306a36Sopenharmony_ci 49562306a36Sopenharmony_ci if ((dev->dev->board_vendor == SSB_BOARDVENDOR_BCM) && 49662306a36Sopenharmony_ci (dev->dev->board_type == SSB_BOARD_BU4306)) 49762306a36Sopenharmony_ci return; /* No software txpower adjustment needed */ 49862306a36Sopenharmony_ci 49962306a36Sopenharmony_ci result = phy->ops->recalc_txpower(dev, !!(flags & B43_TXPWR_IGNORE_TSSI)); 50062306a36Sopenharmony_ci if (result == B43_TXPWR_RES_DONE) 50162306a36Sopenharmony_ci return; /* We are done. */ 50262306a36Sopenharmony_ci B43_WARN_ON(result != B43_TXPWR_RES_NEED_ADJUST); 50362306a36Sopenharmony_ci B43_WARN_ON(phy->ops->adjust_txpower == NULL); 50462306a36Sopenharmony_ci 50562306a36Sopenharmony_ci /* We must adjust the transmission power in hardware. 50662306a36Sopenharmony_ci * Schedule b43_phy_txpower_adjust_work(). */ 50762306a36Sopenharmony_ci ieee80211_queue_work(dev->wl->hw, &dev->wl->txpower_adjust_work); 50862306a36Sopenharmony_ci} 50962306a36Sopenharmony_ci 51062306a36Sopenharmony_ciint b43_phy_shm_tssi_read(struct b43_wldev *dev, u16 shm_offset) 51162306a36Sopenharmony_ci{ 51262306a36Sopenharmony_ci const bool is_ofdm = (shm_offset != B43_SHM_SH_TSSI_CCK); 51362306a36Sopenharmony_ci unsigned int a, b, c, d; 51462306a36Sopenharmony_ci unsigned int average; 51562306a36Sopenharmony_ci u32 tmp; 51662306a36Sopenharmony_ci 51762306a36Sopenharmony_ci tmp = b43_shm_read32(dev, B43_SHM_SHARED, shm_offset); 51862306a36Sopenharmony_ci a = tmp & 0xFF; 51962306a36Sopenharmony_ci b = (tmp >> 8) & 0xFF; 52062306a36Sopenharmony_ci c = (tmp >> 16) & 0xFF; 52162306a36Sopenharmony_ci d = (tmp >> 24) & 0xFF; 52262306a36Sopenharmony_ci if (a == 0 || a == B43_TSSI_MAX || 52362306a36Sopenharmony_ci b == 0 || b == B43_TSSI_MAX || 52462306a36Sopenharmony_ci c == 0 || c == B43_TSSI_MAX || 52562306a36Sopenharmony_ci d == 0 || d == B43_TSSI_MAX) 52662306a36Sopenharmony_ci return -ENOENT; 52762306a36Sopenharmony_ci /* The values are OK. Clear them. */ 52862306a36Sopenharmony_ci tmp = B43_TSSI_MAX | (B43_TSSI_MAX << 8) | 52962306a36Sopenharmony_ci (B43_TSSI_MAX << 16) | (B43_TSSI_MAX << 24); 53062306a36Sopenharmony_ci b43_shm_write32(dev, B43_SHM_SHARED, shm_offset, tmp); 53162306a36Sopenharmony_ci 53262306a36Sopenharmony_ci if (is_ofdm) { 53362306a36Sopenharmony_ci a = (a + 32) & 0x3F; 53462306a36Sopenharmony_ci b = (b + 32) & 0x3F; 53562306a36Sopenharmony_ci c = (c + 32) & 0x3F; 53662306a36Sopenharmony_ci d = (d + 32) & 0x3F; 53762306a36Sopenharmony_ci } 53862306a36Sopenharmony_ci 53962306a36Sopenharmony_ci /* Get the average of the values with 0.5 added to each value. */ 54062306a36Sopenharmony_ci average = (a + b + c + d + 2) / 4; 54162306a36Sopenharmony_ci if (is_ofdm) { 54262306a36Sopenharmony_ci /* Adjust for CCK-boost */ 54362306a36Sopenharmony_ci if (b43_shm_read16(dev, B43_SHM_SHARED, B43_SHM_SH_HOSTF1) 54462306a36Sopenharmony_ci & B43_HF_CCKBOOST) 54562306a36Sopenharmony_ci average = (average >= 13) ? (average - 13) : 0; 54662306a36Sopenharmony_ci } 54762306a36Sopenharmony_ci 54862306a36Sopenharmony_ci return average; 54962306a36Sopenharmony_ci} 55062306a36Sopenharmony_ci 55162306a36Sopenharmony_civoid b43_phyop_switch_analog_generic(struct b43_wldev *dev, bool on) 55262306a36Sopenharmony_ci{ 55362306a36Sopenharmony_ci b43_write16(dev, B43_MMIO_PHY0, on ? 0 : 0xF4); 55462306a36Sopenharmony_ci} 55562306a36Sopenharmony_ci 55662306a36Sopenharmony_ci 55762306a36Sopenharmony_cibool b43_is_40mhz(struct b43_wldev *dev) 55862306a36Sopenharmony_ci{ 55962306a36Sopenharmony_ci return dev->phy.chandef->width == NL80211_CHAN_WIDTH_40; 56062306a36Sopenharmony_ci} 56162306a36Sopenharmony_ci 56262306a36Sopenharmony_ci/* https://bcm-v4.sipsolutions.net/802.11/PHY/N/BmacPhyClkFgc */ 56362306a36Sopenharmony_civoid b43_phy_force_clock(struct b43_wldev *dev, bool force) 56462306a36Sopenharmony_ci{ 56562306a36Sopenharmony_ci u32 tmp; 56662306a36Sopenharmony_ci 56762306a36Sopenharmony_ci WARN_ON(dev->phy.type != B43_PHYTYPE_N && 56862306a36Sopenharmony_ci dev->phy.type != B43_PHYTYPE_HT && 56962306a36Sopenharmony_ci dev->phy.type != B43_PHYTYPE_AC); 57062306a36Sopenharmony_ci 57162306a36Sopenharmony_ci switch (dev->dev->bus_type) { 57262306a36Sopenharmony_ci#ifdef CONFIG_B43_BCMA 57362306a36Sopenharmony_ci case B43_BUS_BCMA: 57462306a36Sopenharmony_ci tmp = bcma_aread32(dev->dev->bdev, BCMA_IOCTL); 57562306a36Sopenharmony_ci if (force) 57662306a36Sopenharmony_ci tmp |= BCMA_IOCTL_FGC; 57762306a36Sopenharmony_ci else 57862306a36Sopenharmony_ci tmp &= ~BCMA_IOCTL_FGC; 57962306a36Sopenharmony_ci bcma_awrite32(dev->dev->bdev, BCMA_IOCTL, tmp); 58062306a36Sopenharmony_ci break; 58162306a36Sopenharmony_ci#endif 58262306a36Sopenharmony_ci#ifdef CONFIG_B43_SSB 58362306a36Sopenharmony_ci case B43_BUS_SSB: 58462306a36Sopenharmony_ci tmp = ssb_read32(dev->dev->sdev, SSB_TMSLOW); 58562306a36Sopenharmony_ci if (force) 58662306a36Sopenharmony_ci tmp |= SSB_TMSLOW_FGC; 58762306a36Sopenharmony_ci else 58862306a36Sopenharmony_ci tmp &= ~SSB_TMSLOW_FGC; 58962306a36Sopenharmony_ci ssb_write32(dev->dev->sdev, SSB_TMSLOW, tmp); 59062306a36Sopenharmony_ci break; 59162306a36Sopenharmony_ci#endif 59262306a36Sopenharmony_ci } 59362306a36Sopenharmony_ci} 594