Linux Audio

Check our new training course

Linux BSP development engineering services

Need help to port Linux and bootloaders to your hardware?
Loading...
Note: File does not exist in v6.8.
  1/*
  2
  3  Broadcom B43 wireless driver
  4  Common PHY routines
  5
  6  Copyright (c) 2005 Martin Langer <martin-langer@gmx.de>,
  7  Copyright (c) 2005-2007 Stefano Brivio <stefano.brivio@polimi.it>
  8  Copyright (c) 2005-2008 Michael Buesch <m@bues.ch>
  9  Copyright (c) 2005, 2006 Danny van Dyk <kugelfang@gentoo.org>
 10  Copyright (c) 2005, 2006 Andreas Jaggi <andreas.jaggi@waterwave.ch>
 11
 12  This program is free software; you can redistribute it and/or modify
 13  it under the terms of the GNU General Public License as published by
 14  the Free Software Foundation; either version 2 of the License, or
 15  (at your option) any later version.
 16
 17  This program is distributed in the hope that it will be useful,
 18  but WITHOUT ANY WARRANTY; without even the implied warranty of
 19  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 20  GNU General Public License for more details.
 21
 22  You should have received a copy of the GNU General Public License
 23  along with this program; see the file COPYING.  If not, write to
 24  the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor,
 25  Boston, MA 02110-1301, USA.
 26
 27*/
 28
 29#include "phy_common.h"
 30#include "phy_g.h"
 31#include "phy_a.h"
 32#include "phy_n.h"
 33#include "phy_lp.h"
 34#include "phy_ht.h"
 35#include "phy_lcn.h"
 36#include "b43.h"
 37#include "main.h"
 38
 39
 40int b43_phy_allocate(struct b43_wldev *dev)
 41{
 42	struct b43_phy *phy = &(dev->phy);
 43	int err;
 44
 45	phy->ops = NULL;
 46
 47	switch (phy->type) {
 48	case B43_PHYTYPE_A:
 49		phy->ops = &b43_phyops_a;
 50		break;
 51	case B43_PHYTYPE_G:
 52		phy->ops = &b43_phyops_g;
 53		break;
 54	case B43_PHYTYPE_N:
 55#ifdef CONFIG_B43_PHY_N
 56		phy->ops = &b43_phyops_n;
 57#endif
 58		break;
 59	case B43_PHYTYPE_LP:
 60#ifdef CONFIG_B43_PHY_LP
 61		phy->ops = &b43_phyops_lp;
 62#endif
 63		break;
 64	case B43_PHYTYPE_HT:
 65#ifdef CONFIG_B43_PHY_HT
 66		phy->ops = &b43_phyops_ht;
 67#endif
 68		break;
 69	case B43_PHYTYPE_LCN:
 70#ifdef CONFIG_B43_PHY_LCN
 71		phy->ops = &b43_phyops_lcn;
 72#endif
 73		break;
 74	}
 75	if (B43_WARN_ON(!phy->ops))
 76		return -ENODEV;
 77
 78	err = phy->ops->allocate(dev);
 79	if (err)
 80		phy->ops = NULL;
 81
 82	return err;
 83}
 84
 85void b43_phy_free(struct b43_wldev *dev)
 86{
 87	dev->phy.ops->free(dev);
 88	dev->phy.ops = NULL;
 89}
 90
 91int b43_phy_init(struct b43_wldev *dev)
 92{
 93	struct b43_phy *phy = &dev->phy;
 94	const struct b43_phy_operations *ops = phy->ops;
 95	int err;
 96
 97	phy->channel = ops->get_default_chan(dev);
 98
 99	ops->software_rfkill(dev, false);
100	err = ops->init(dev);
101	if (err) {
102		b43err(dev->wl, "PHY init failed\n");
103		goto err_block_rf;
104	}
105	/* Make sure to switch hardware and firmware (SHM) to
106	 * the default channel. */
107	err = b43_switch_channel(dev, ops->get_default_chan(dev));
108	if (err) {
109		b43err(dev->wl, "PHY init: Channel switch to default failed\n");
110		goto err_phy_exit;
111	}
112
113	return 0;
114
115err_phy_exit:
116	if (ops->exit)
117		ops->exit(dev);
118err_block_rf:
119	ops->software_rfkill(dev, true);
120
121	return err;
122}
123
124void b43_phy_exit(struct b43_wldev *dev)
125{
126	const struct b43_phy_operations *ops = dev->phy.ops;
127
128	ops->software_rfkill(dev, true);
129	if (ops->exit)
130		ops->exit(dev);
131}
132
133bool b43_has_hardware_pctl(struct b43_wldev *dev)
134{
135	if (!dev->phy.hardware_power_control)
136		return 0;
137	if (!dev->phy.ops->supports_hwpctl)
138		return 0;
139	return dev->phy.ops->supports_hwpctl(dev);
140}
141
142void b43_radio_lock(struct b43_wldev *dev)
143{
144	u32 macctl;
145
146#if B43_DEBUG
147	B43_WARN_ON(dev->phy.radio_locked);
148	dev->phy.radio_locked = 1;
149#endif
150
151	macctl = b43_read32(dev, B43_MMIO_MACCTL);
152	macctl |= B43_MACCTL_RADIOLOCK;
153	b43_write32(dev, B43_MMIO_MACCTL, macctl);
154	/* Commit the write and wait for the firmware
155	 * to finish any radio register access. */
156	b43_read32(dev, B43_MMIO_MACCTL);
157	udelay(10);
158}
159
160void b43_radio_unlock(struct b43_wldev *dev)
161{
162	u32 macctl;
163
164#if B43_DEBUG
165	B43_WARN_ON(!dev->phy.radio_locked);
166	dev->phy.radio_locked = 0;
167#endif
168
169	/* Commit any write */
170	b43_read16(dev, B43_MMIO_PHY_VER);
171	/* unlock */
172	macctl = b43_read32(dev, B43_MMIO_MACCTL);
173	macctl &= ~B43_MACCTL_RADIOLOCK;
174	b43_write32(dev, B43_MMIO_MACCTL, macctl);
175}
176
177void b43_phy_lock(struct b43_wldev *dev)
178{
179#if B43_DEBUG
180	B43_WARN_ON(dev->phy.phy_locked);
181	dev->phy.phy_locked = 1;
182#endif
183	B43_WARN_ON(dev->dev->core_rev < 3);
184
185	if (!b43_is_mode(dev->wl, NL80211_IFTYPE_AP))
186		b43_power_saving_ctl_bits(dev, B43_PS_AWAKE);
187}
188
189void b43_phy_unlock(struct b43_wldev *dev)
190{
191#if B43_DEBUG
192	B43_WARN_ON(!dev->phy.phy_locked);
193	dev->phy.phy_locked = 0;
194#endif
195	B43_WARN_ON(dev->dev->core_rev < 3);
196
197	if (!b43_is_mode(dev->wl, NL80211_IFTYPE_AP))
198		b43_power_saving_ctl_bits(dev, 0);
199}
200
201static inline void assert_mac_suspended(struct b43_wldev *dev)
202{
203	if (!B43_DEBUG)
204		return;
205	if ((b43_status(dev) >= B43_STAT_INITIALIZED) &&
206	    (dev->mac_suspended <= 0)) {
207		b43dbg(dev->wl, "PHY/RADIO register access with "
208		       "enabled MAC.\n");
209		dump_stack();
210	}
211}
212
213u16 b43_radio_read(struct b43_wldev *dev, u16 reg)
214{
215	assert_mac_suspended(dev);
216	return dev->phy.ops->radio_read(dev, reg);
217}
218
219void b43_radio_write(struct b43_wldev *dev, u16 reg, u16 value)
220{
221	assert_mac_suspended(dev);
222	dev->phy.ops->radio_write(dev, reg, value);
223}
224
225void b43_radio_mask(struct b43_wldev *dev, u16 offset, u16 mask)
226{
227	b43_radio_write16(dev, offset,
228			  b43_radio_read16(dev, offset) & mask);
229}
230
231void b43_radio_set(struct b43_wldev *dev, u16 offset, u16 set)
232{
233	b43_radio_write16(dev, offset,
234			  b43_radio_read16(dev, offset) | set);
235}
236
237void b43_radio_maskset(struct b43_wldev *dev, u16 offset, u16 mask, u16 set)
238{
239	b43_radio_write16(dev, offset,
240			  (b43_radio_read16(dev, offset) & mask) | set);
241}
242
243u16 b43_phy_read(struct b43_wldev *dev, u16 reg)
244{
245	assert_mac_suspended(dev);
246	dev->phy.writes_counter = 0;
247	return dev->phy.ops->phy_read(dev, reg);
248}
249
250void b43_phy_write(struct b43_wldev *dev, u16 reg, u16 value)
251{
252	assert_mac_suspended(dev);
253	dev->phy.ops->phy_write(dev, reg, value);
254	if (++dev->phy.writes_counter == B43_MAX_WRITES_IN_ROW) {
255		b43_read16(dev, B43_MMIO_PHY_VER);
256		dev->phy.writes_counter = 0;
257	}
258}
259
260void b43_phy_copy(struct b43_wldev *dev, u16 destreg, u16 srcreg)
261{
262	assert_mac_suspended(dev);
263	dev->phy.ops->phy_write(dev, destreg,
264		dev->phy.ops->phy_read(dev, srcreg));
265}
266
267void b43_phy_mask(struct b43_wldev *dev, u16 offset, u16 mask)
268{
269	if (dev->phy.ops->phy_maskset) {
270		assert_mac_suspended(dev);
271		dev->phy.ops->phy_maskset(dev, offset, mask, 0);
272	} else {
273		b43_phy_write(dev, offset,
274			      b43_phy_read(dev, offset) & mask);
275	}
276}
277
278void b43_phy_set(struct b43_wldev *dev, u16 offset, u16 set)
279{
280	if (dev->phy.ops->phy_maskset) {
281		assert_mac_suspended(dev);
282		dev->phy.ops->phy_maskset(dev, offset, 0xFFFF, set);
283	} else {
284		b43_phy_write(dev, offset,
285			      b43_phy_read(dev, offset) | set);
286	}
287}
288
289void b43_phy_maskset(struct b43_wldev *dev, u16 offset, u16 mask, u16 set)
290{
291	if (dev->phy.ops->phy_maskset) {
292		assert_mac_suspended(dev);
293		dev->phy.ops->phy_maskset(dev, offset, mask, set);
294	} else {
295		b43_phy_write(dev, offset,
296			      (b43_phy_read(dev, offset) & mask) | set);
297	}
298}
299
300int b43_switch_channel(struct b43_wldev *dev, unsigned int new_channel)
301{
302	struct b43_phy *phy = &(dev->phy);
303	u16 channelcookie, savedcookie;
304	int err;
305
306	if (new_channel == B43_DEFAULT_CHANNEL)
307		new_channel = phy->ops->get_default_chan(dev);
308
309	/* First we set the channel radio code to prevent the
310	 * firmware from sending ghost packets.
311	 */
312	channelcookie = new_channel;
313	if (b43_current_band(dev->wl) == IEEE80211_BAND_5GHZ)
314		channelcookie |= B43_SHM_SH_CHAN_5GHZ;
315	/* FIXME: set 40Mhz flag if required */
316	if (0)
317		channelcookie |= B43_SHM_SH_CHAN_40MHZ;
318	savedcookie = b43_shm_read16(dev, B43_SHM_SHARED, B43_SHM_SH_CHAN);
319	b43_shm_write16(dev, B43_SHM_SHARED, B43_SHM_SH_CHAN, channelcookie);
320
321	/* Now try to switch the PHY hardware channel. */
322	err = phy->ops->switch_channel(dev, new_channel);
323	if (err)
324		goto err_restore_cookie;
325
326	dev->phy.channel = new_channel;
327	/* Wait for the radio to tune to the channel and stabilize. */
328	msleep(8);
329
330	return 0;
331
332err_restore_cookie:
333	b43_shm_write16(dev, B43_SHM_SHARED,
334			B43_SHM_SH_CHAN, savedcookie);
335
336	return err;
337}
338
339void b43_software_rfkill(struct b43_wldev *dev, bool blocked)
340{
341	struct b43_phy *phy = &dev->phy;
342
343	b43_mac_suspend(dev);
344	phy->ops->software_rfkill(dev, blocked);
345	phy->radio_on = !blocked;
346	b43_mac_enable(dev);
347}
348
349/**
350 * b43_phy_txpower_adjust_work - TX power workqueue.
351 *
352 * Workqueue for updating the TX power parameters in hardware.
353 */
354void b43_phy_txpower_adjust_work(struct work_struct *work)
355{
356	struct b43_wl *wl = container_of(work, struct b43_wl,
357					 txpower_adjust_work);
358	struct b43_wldev *dev;
359
360	mutex_lock(&wl->mutex);
361	dev = wl->current_dev;
362
363	if (likely(dev && (b43_status(dev) >= B43_STAT_STARTED)))
364		dev->phy.ops->adjust_txpower(dev);
365
366	mutex_unlock(&wl->mutex);
367}
368
369void b43_phy_txpower_check(struct b43_wldev *dev, unsigned int flags)
370{
371	struct b43_phy *phy = &dev->phy;
372	unsigned long now = jiffies;
373	enum b43_txpwr_result result;
374
375	if (!(flags & B43_TXPWR_IGNORE_TIME)) {
376		/* Check if it's time for a TXpower check. */
377		if (time_before(now, phy->next_txpwr_check_time))
378			return; /* Not yet */
379	}
380	/* The next check will be needed in two seconds, or later. */
381	phy->next_txpwr_check_time = round_jiffies(now + (HZ * 2));
382
383	if ((dev->dev->board_vendor == SSB_BOARDVENDOR_BCM) &&
384	    (dev->dev->board_type == SSB_BOARD_BU4306))
385		return; /* No software txpower adjustment needed */
386
387	result = phy->ops->recalc_txpower(dev, !!(flags & B43_TXPWR_IGNORE_TSSI));
388	if (result == B43_TXPWR_RES_DONE)
389		return; /* We are done. */
390	B43_WARN_ON(result != B43_TXPWR_RES_NEED_ADJUST);
391	B43_WARN_ON(phy->ops->adjust_txpower == NULL);
392
393	/* We must adjust the transmission power in hardware.
394	 * Schedule b43_phy_txpower_adjust_work(). */
395	ieee80211_queue_work(dev->wl->hw, &dev->wl->txpower_adjust_work);
396}
397
398int b43_phy_shm_tssi_read(struct b43_wldev *dev, u16 shm_offset)
399{
400	const bool is_ofdm = (shm_offset != B43_SHM_SH_TSSI_CCK);
401	unsigned int a, b, c, d;
402	unsigned int average;
403	u32 tmp;
404
405	tmp = b43_shm_read32(dev, B43_SHM_SHARED, shm_offset);
406	a = tmp & 0xFF;
407	b = (tmp >> 8) & 0xFF;
408	c = (tmp >> 16) & 0xFF;
409	d = (tmp >> 24) & 0xFF;
410	if (a == 0 || a == B43_TSSI_MAX ||
411	    b == 0 || b == B43_TSSI_MAX ||
412	    c == 0 || c == B43_TSSI_MAX ||
413	    d == 0 || d == B43_TSSI_MAX)
414		return -ENOENT;
415	/* The values are OK. Clear them. */
416	tmp = B43_TSSI_MAX | (B43_TSSI_MAX << 8) |
417	      (B43_TSSI_MAX << 16) | (B43_TSSI_MAX << 24);
418	b43_shm_write32(dev, B43_SHM_SHARED, shm_offset, tmp);
419
420	if (is_ofdm) {
421		a = (a + 32) & 0x3F;
422		b = (b + 32) & 0x3F;
423		c = (c + 32) & 0x3F;
424		d = (d + 32) & 0x3F;
425	}
426
427	/* Get the average of the values with 0.5 added to each value. */
428	average = (a + b + c + d + 2) / 4;
429	if (is_ofdm) {
430		/* Adjust for CCK-boost */
431		if (b43_shm_read16(dev, B43_SHM_SHARED, B43_SHM_SH_HOSTFLO)
432		    & B43_HF_CCKBOOST)
433			average = (average >= 13) ? (average - 13) : 0;
434	}
435
436	return average;
437}
438
439void b43_phyop_switch_analog_generic(struct b43_wldev *dev, bool on)
440{
441	b43_write16(dev, B43_MMIO_PHY0, on ? 0 : 0xF4);
442}
443
444
445bool b43_channel_type_is_40mhz(enum nl80211_channel_type channel_type)
446{
447	return (channel_type == NL80211_CHAN_HT40MINUS ||
448		channel_type == NL80211_CHAN_HT40PLUS);
449}
450
451/* http://bcm-v4.sipsolutions.net/802.11/PHY/Cordic */
452struct b43_c32 b43_cordic(int theta)
453{
454	static const u32 arctg[] = {
455		2949120, 1740967, 919879, 466945, 234379, 117304,
456		  58666,   29335,  14668,   7334,   3667,   1833,
457		    917,     458,    229,    115,     57,     29,
458	};
459	u8 i;
460	s32 tmp;
461	s8 signx = 1;
462	u32 angle = 0;
463	struct b43_c32 ret = { .i = 39797, .q = 0, };
464
465	while (theta > (180 << 16))
466		theta -= (360 << 16);
467	while (theta < -(180 << 16))
468		theta += (360 << 16);
469
470	if (theta > (90 << 16)) {
471		theta -= (180 << 16);
472		signx = -1;
473	} else if (theta < -(90 << 16)) {
474		theta += (180 << 16);
475		signx = -1;
476	}
477
478	for (i = 0; i <= 17; i++) {
479		if (theta > angle) {
480			tmp = ret.i - (ret.q >> i);
481			ret.q += ret.i >> i;
482			ret.i = tmp;
483			angle += arctg[i];
484		} else {
485			tmp = ret.i + (ret.q >> i);
486			ret.q -= ret.i >> i;
487			ret.i = tmp;
488			angle -= arctg[i];
489		}
490	}
491
492	ret.i *= signx;
493	ret.q *= signx;
494
495	return ret;
496}