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