⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 bcm43xx_power.c

📁 linux内核源码
💻 C
字号:
/*  Broadcom BCM43xx wireless driver  Copyright (c) 2005 Martin Langer <martin-langer@gmx.de>,                     Stefano Brivio <st3@riseup.net>                     Michael Buesch <mbuesch@freenet.de>                     Danny van Dyk <kugelfang@gentoo.org>                     Andreas Jaggi <andreas.jaggi@waterwave.ch>  Some parts of the code in this file are derived from the ipw2200  driver  Copyright(c) 2003 - 2004 Intel Corporation.  This program is free software; you can redistribute it and/or modify  it under the terms of the GNU General Public License as published by  the Free Software Foundation; either version 2 of the License, or  (at your option) any later version.  This program is distributed in the hope that it will be useful,  but WITHOUT ANY WARRANTY; without even the implied warranty of  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the  GNU General Public License for more details.  You should have received a copy of the GNU General Public License  along with this program; see the file COPYING.  If not, write to  the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor,  Boston, MA 02110-1301, USA.*/#include <linux/delay.h>#include "bcm43xx.h"#include "bcm43xx_power.h"#include "bcm43xx_main.h"/* Get the Slow Clock Source */static int bcm43xx_pctl_get_slowclksrc(struct bcm43xx_private *bcm){	u32 tmp;	int err;	assert(bcm->current_core == &bcm->core_chipcommon);	if (bcm->current_core->rev < 6) {		if (bcm->bustype == BCM43xx_BUSTYPE_PCMCIA ||		    bcm->bustype == BCM43xx_BUSTYPE_SB)			return BCM43xx_PCTL_CLKSRC_XTALOS;		if (bcm->bustype == BCM43xx_BUSTYPE_PCI) {			err = bcm43xx_pci_read_config32(bcm, BCM43xx_PCTL_OUT, &tmp);			assert(!err);			if (tmp & 0x10)				return BCM43xx_PCTL_CLKSRC_PCI;			return BCM43xx_PCTL_CLKSRC_XTALOS;		}	}	if (bcm->current_core->rev < 10) {		tmp = bcm43xx_read32(bcm, BCM43xx_CHIPCOMMON_SLOWCLKCTL);		tmp &= 0x7;		if (tmp == 0)			return BCM43xx_PCTL_CLKSRC_LOPWROS;		if (tmp == 1)			return BCM43xx_PCTL_CLKSRC_XTALOS;		if (tmp == 2)			return BCM43xx_PCTL_CLKSRC_PCI;	}	return BCM43xx_PCTL_CLKSRC_XTALOS;}/* Get max/min slowclock frequency * as described in http://bcm-specs.sipsolutions.net/PowerControl */static int bcm43xx_pctl_clockfreqlimit(struct bcm43xx_private *bcm,				       int get_max){	int limit;	int clocksrc;	int divisor;	u32 tmp;	assert(bcm->chipcommon_capabilities & BCM43xx_CAPABILITIES_PCTL);	assert(bcm->current_core == &bcm->core_chipcommon);	clocksrc = bcm43xx_pctl_get_slowclksrc(bcm);	if (bcm->current_core->rev < 6) {		switch (clocksrc) {		case BCM43xx_PCTL_CLKSRC_PCI:			divisor = 64;			break;		case BCM43xx_PCTL_CLKSRC_XTALOS:			divisor = 32;			break;		default:			assert(0);			divisor = 1;		}	} else if (bcm->current_core->rev < 10) {		switch (clocksrc) {		case BCM43xx_PCTL_CLKSRC_LOPWROS:			divisor = 1;			break;		case BCM43xx_PCTL_CLKSRC_XTALOS:		case BCM43xx_PCTL_CLKSRC_PCI:			tmp = bcm43xx_read32(bcm, BCM43xx_CHIPCOMMON_SLOWCLKCTL);			divisor = ((tmp & 0xFFFF0000) >> 16) + 1;			divisor *= 4;			break;		default:			assert(0);			divisor = 1;		}	} else {		tmp = bcm43xx_read32(bcm, BCM43xx_CHIPCOMMON_SYSCLKCTL);		divisor = ((tmp & 0xFFFF0000) >> 16) + 1;		divisor *= 4;	}	switch (clocksrc) {	case BCM43xx_PCTL_CLKSRC_LOPWROS:		if (get_max)			limit = 43000;		else			limit = 25000;		break;	case BCM43xx_PCTL_CLKSRC_XTALOS:		if (get_max)			limit = 20200000;		else			limit = 19800000;		break;	case BCM43xx_PCTL_CLKSRC_PCI:		if (get_max)			limit = 34000000;		else			limit = 25000000;		break;	default:		assert(0);		limit = 0;	}	limit /= divisor;	return limit;}/* init power control * as described in http://bcm-specs.sipsolutions.net/PowerControl */int bcm43xx_pctl_init(struct bcm43xx_private *bcm){	int err, maxfreq;	struct bcm43xx_coreinfo *old_core;	old_core = bcm->current_core;	err = bcm43xx_switch_core(bcm, &bcm->core_chipcommon);	if (err == -ENODEV)		return 0;	if (err)		goto out;	if (bcm->chip_id == 0x4321) {		if (bcm->chip_rev == 0)			bcm43xx_write32(bcm, BCM43xx_CHIPCOMMON_CTL, 0x03A4);		if (bcm->chip_rev == 1)			bcm43xx_write32(bcm, BCM43xx_CHIPCOMMON_CTL, 0x00A4);	}	if (bcm->chipcommon_capabilities & BCM43xx_CAPABILITIES_PCTL) {		if (bcm->current_core->rev >= 10) {			/* Set Idle Power clock rate to 1Mhz */			bcm43xx_write32(bcm, BCM43xx_CHIPCOMMON_SYSCLKCTL,				       (bcm43xx_read32(bcm, BCM43xx_CHIPCOMMON_SYSCLKCTL)				       & 0x0000FFFF) | 0x40000);		} else {			maxfreq = bcm43xx_pctl_clockfreqlimit(bcm, 1);			bcm43xx_write32(bcm, BCM43xx_CHIPCOMMON_PLLONDELAY,				       (maxfreq * 150 + 999999) / 1000000);			bcm43xx_write32(bcm, BCM43xx_CHIPCOMMON_FREFSELDELAY,				       (maxfreq * 15 + 999999) / 1000000);		}	}	err = bcm43xx_switch_core(bcm, old_core);	assert(err == 0);out:	return err;}u16 bcm43xx_pctl_powerup_delay(struct bcm43xx_private *bcm){	u16 delay = 0;	int err;	u32 pll_on_delay;	struct bcm43xx_coreinfo *old_core;	int minfreq;	if (bcm->bustype != BCM43xx_BUSTYPE_PCI)		goto out;	if (!(bcm->chipcommon_capabilities & BCM43xx_CAPABILITIES_PCTL))		goto out;	old_core = bcm->current_core;	err = bcm43xx_switch_core(bcm, &bcm->core_chipcommon);	if (err == -ENODEV)		goto out;	minfreq = bcm43xx_pctl_clockfreqlimit(bcm, 0);	pll_on_delay = bcm43xx_read32(bcm, BCM43xx_CHIPCOMMON_PLLONDELAY);	delay = (((pll_on_delay + 2) * 1000000) + (minfreq - 1)) / minfreq;	err = bcm43xx_switch_core(bcm, old_core);	assert(err == 0);out:	return delay;}/* set the powercontrol clock * as described in http://bcm-specs.sipsolutions.net/PowerControl */int bcm43xx_pctl_set_clock(struct bcm43xx_private *bcm, u16 mode){	int err;	struct bcm43xx_coreinfo *old_core;	u32 tmp;	old_core = bcm->current_core;	err = bcm43xx_switch_core(bcm, &bcm->core_chipcommon);	if (err == -ENODEV)		return 0;	if (err)		goto out;		if (bcm->core_chipcommon.rev < 6) {		if (mode == BCM43xx_PCTL_CLK_FAST) {			err = bcm43xx_pctl_set_crystal(bcm, 1);			if (err)				goto out;		}	} else {		if ((bcm->chipcommon_capabilities & BCM43xx_CAPABILITIES_PCTL) &&			(bcm->core_chipcommon.rev < 10)) {			switch (mode) {			case BCM43xx_PCTL_CLK_FAST:				tmp = bcm43xx_read32(bcm, BCM43xx_CHIPCOMMON_SLOWCLKCTL);				tmp = (tmp & ~BCM43xx_PCTL_FORCE_SLOW) | BCM43xx_PCTL_FORCE_PLL;				bcm43xx_write32(bcm, BCM43xx_CHIPCOMMON_SLOWCLKCTL, tmp);				break;			case BCM43xx_PCTL_CLK_SLOW:				tmp = bcm43xx_read32(bcm, BCM43xx_CHIPCOMMON_SLOWCLKCTL);				tmp |= BCM43xx_PCTL_FORCE_SLOW;				bcm43xx_write32(bcm, BCM43xx_CHIPCOMMON_SLOWCLKCTL, tmp);				break;			case BCM43xx_PCTL_CLK_DYNAMIC:				tmp = bcm43xx_read32(bcm, BCM43xx_CHIPCOMMON_SLOWCLKCTL);				tmp &= ~BCM43xx_PCTL_FORCE_SLOW;				tmp |= BCM43xx_PCTL_FORCE_PLL;				tmp &= ~BCM43xx_PCTL_DYN_XTAL;				bcm43xx_write32(bcm, BCM43xx_CHIPCOMMON_SLOWCLKCTL, tmp);			}		}	}		err = bcm43xx_switch_core(bcm, old_core);	assert(err == 0);out:	return err;}int bcm43xx_pctl_set_crystal(struct bcm43xx_private *bcm, int on){	int err;	u32 in, out, outenable;	err = bcm43xx_pci_read_config32(bcm, BCM43xx_PCTL_IN, &in);	if (err)		goto err_pci;	err = bcm43xx_pci_read_config32(bcm, BCM43xx_PCTL_OUT, &out);	if (err)		goto err_pci;	err = bcm43xx_pci_read_config32(bcm, BCM43xx_PCTL_OUTENABLE, &outenable);	if (err)		goto err_pci;	outenable |= (BCM43xx_PCTL_XTAL_POWERUP | BCM43xx_PCTL_PLL_POWERDOWN);	if (on) {		if (in & 0x40)			return 0;		out |= (BCM43xx_PCTL_XTAL_POWERUP | BCM43xx_PCTL_PLL_POWERDOWN);		err = bcm43xx_pci_write_config32(bcm, BCM43xx_PCTL_OUT, out);		if (err)			goto err_pci;		err = bcm43xx_pci_write_config32(bcm, BCM43xx_PCTL_OUTENABLE, outenable);		if (err)			goto err_pci;		udelay(1000);		out &= ~BCM43xx_PCTL_PLL_POWERDOWN;		err = bcm43xx_pci_write_config32(bcm, BCM43xx_PCTL_OUT, out);		if (err)			goto err_pci;		udelay(5000);	} else {		if (bcm->current_core->rev < 5)			return 0;		if (bcm->sprom.boardflags & BCM43xx_BFL_XTAL_NOSLOW)			return 0;/*		XXX: Why BCM43xx_MMIO_RADIO_HWENABLED_xx can't be read at this time? *		err = bcm43xx_switch_core(bcm, bcm->active_80211_core); *		if (err) *			return err; *		if (((bcm->current_core->rev >= 3) && *			(bcm43xx_read32(bcm, BCM43xx_MMIO_RADIO_HWENABLED_HI) & (1 << 16))) || *		      ((bcm->current_core->rev < 3) && *			!(bcm43xx_read16(bcm, BCM43xx_MMIO_RADIO_HWENABLED_LO) & (1 << 4)))) *			return 0; *		err = bcm43xx_switch_core(bcm, &bcm->core_chipcommon); *		if (err) *			return err; */				err = bcm43xx_pctl_set_clock(bcm, BCM43xx_PCTL_CLK_SLOW);		if (err)			goto out;		out &= ~BCM43xx_PCTL_XTAL_POWERUP;		out |= BCM43xx_PCTL_PLL_POWERDOWN;		err = bcm43xx_pci_write_config32(bcm, BCM43xx_PCTL_OUT, out);		if (err)			goto err_pci;		err = bcm43xx_pci_write_config32(bcm, BCM43xx_PCTL_OUTENABLE, outenable);		if (err)			goto err_pci;	}out:	return err;err_pci:	printk(KERN_ERR PFX "Error: pctl_set_clock() could not access PCI config space!\n");	err = -EBUSY;	goto out;}/* Set the PowerSavingControlBits. * Bitvalues: *   0  => unset the bit *   1  => set the bit *   -1 => calculate the bit */void bcm43xx_power_saving_ctl_bits(struct bcm43xx_private *bcm,				   int bit25, int bit26){	int i;	u32 status;//FIXME: Force 25 to off and 26 to on for now:bit25 = 0;bit26 = 1;	if (bit25 == -1) {		//TODO: If powersave is not off and FIXME is not set and we are not in adhoc		//	and thus is not an AP and we are associated, set bit 25	}	if (bit26 == -1) {		//TODO: If the device is awake or this is an AP, or we are scanning, or FIXME,		//	or we are associated, or FIXME, or the latest PS-Poll packet sent was		//	successful, set bit26	}	status = bcm43xx_read32(bcm, BCM43xx_MMIO_STATUS_BITFIELD);	if (bit25)		status |= BCM43xx_SBF_PS1;	else		status &= ~BCM43xx_SBF_PS1;	if (bit26)		status |= BCM43xx_SBF_PS2;	else		status &= ~BCM43xx_SBF_PS2;	bcm43xx_write32(bcm, BCM43xx_MMIO_STATUS_BITFIELD, status);	if (bit26 && bcm->current_core->rev >= 5) {		for (i = 0; i < 100; i++) {			if (bcm43xx_shm_read32(bcm, BCM43xx_SHM_SHARED, 0x0040) != 4)				break;			udelay(10);		}	}}

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -