b44.c

来自「Linux Kernel 2.6.9 for OMAP1710」· C语言 代码 · 共 1,937 行 · 第 1/4 页

C
1,937
字号
/* b44.c: Broadcom 4400 device driver. * * Copyright (C) 2002 David S. Miller (davem@redhat.com) * Fixed by Pekka Pietikainen (pp@ee.oulu.fi) * * Distribute under GPL. */#include <linux/kernel.h>#include <linux/module.h>#include <linux/types.h>#include <linux/netdevice.h>#include <linux/ethtool.h>#include <linux/mii.h>#include <linux/if_ether.h>#include <linux/etherdevice.h>#include <linux/pci.h>#include <linux/delay.h>#include <linux/init.h>#include <linux/version.h>#include <asm/uaccess.h>#include <asm/io.h>#include <asm/irq.h>#include "b44.h"#define DRV_MODULE_NAME		"b44"#define PFX DRV_MODULE_NAME	": "#define DRV_MODULE_VERSION	"0.94"#define DRV_MODULE_RELDATE	"May 4, 2004"#define B44_DEF_MSG_ENABLE	  \	(NETIF_MSG_DRV		| \	 NETIF_MSG_PROBE	| \	 NETIF_MSG_LINK		| \	 NETIF_MSG_TIMER	| \	 NETIF_MSG_IFDOWN	| \	 NETIF_MSG_IFUP		| \	 NETIF_MSG_RX_ERR	| \	 NETIF_MSG_TX_ERR)/* length of time before we decide the hardware is borked, * and dev->tx_timeout() should be called to fix the problem */#define B44_TX_TIMEOUT			(5 * HZ)/* hardware minimum and maximum for a single frame's data payload */#define B44_MIN_MTU			60#define B44_MAX_MTU			1500#define B44_RX_RING_SIZE		512#define B44_DEF_RX_RING_PENDING		200#define B44_RX_RING_BYTES	(sizeof(struct dma_desc) * \				 B44_RX_RING_SIZE)#define B44_TX_RING_SIZE		512#define B44_DEF_TX_RING_PENDING		(B44_TX_RING_SIZE - 1)#define B44_TX_RING_BYTES	(sizeof(struct dma_desc) * \				 B44_TX_RING_SIZE)#define TX_RING_GAP(BP)	\	(B44_TX_RING_SIZE - (BP)->tx_pending)#define TX_BUFFS_AVAIL(BP)						\	(((BP)->tx_cons <= (BP)->tx_prod) ?				\	  (BP)->tx_cons + (BP)->tx_pending - (BP)->tx_prod :		\	  (BP)->tx_cons - (BP)->tx_prod - TX_RING_GAP(BP))#define NEXT_TX(N)		(((N) + 1) & (B44_TX_RING_SIZE - 1))#define RX_PKT_BUF_SZ		(1536 + bp->rx_offset + 64)/* minimum number of free TX descriptors required to wake up TX process */#define B44_TX_WAKEUP_THRESH		(B44_TX_RING_SIZE / 4)static char version[] __devinitdata =	DRV_MODULE_NAME ".c:v" DRV_MODULE_VERSION " (" DRV_MODULE_RELDATE ")\n";MODULE_AUTHOR("David S. Miller (davem@redhat.com)");MODULE_DESCRIPTION("Broadcom 4400 10/100 PCI ethernet driver");MODULE_LICENSE("GPL");MODULE_PARM(b44_debug, "i");MODULE_PARM_DESC(b44_debug, "B44 bitmapped debugging message enable value");static int b44_debug = -1;	/* -1 == use B44_DEF_MSG_ENABLE as value */static struct pci_device_id b44_pci_tbl[] = {	{ PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_BCM4401,	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL },	{ PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_BCM4401B0,	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL },	{ PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_BCM4401B1,	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL },	{ }	/* terminate list with empty entry */};MODULE_DEVICE_TABLE(pci, b44_pci_tbl);static void b44_halt(struct b44 *);static void b44_init_rings(struct b44 *);static void b44_init_hw(struct b44 *);static inline unsigned long br32(const struct b44 *bp, unsigned long reg){	return readl(bp->regs + reg);}static inline void bw32(const struct b44 *bp, 			unsigned long reg, unsigned long val){	writel(val, bp->regs + reg);}static int b44_wait_bit(struct b44 *bp, unsigned long reg,			u32 bit, unsigned long timeout, const int clear){	unsigned long i;	for (i = 0; i < timeout; i++) {		u32 val = br32(bp, reg);		if (clear && !(val & bit))			break;		if (!clear && (val & bit))			break;		udelay(10);	}	if (i == timeout) {		printk(KERN_ERR PFX "%s: BUG!  Timeout waiting for bit %08x of register "		       "%lx to %s.\n",		       bp->dev->name,		       bit, reg,		       (clear ? "clear" : "set"));		return -ENODEV;	}	return 0;}/* Sonics SiliconBackplane support routines.  ROFL, you should see all the * buzz words used on this company's website :-) * * All of these routines must be invoked with bp->lock held and * interrupts disabled. */#define SBID_SDRAM		0#define SBID_PCI_MEM		1#define SBID_PCI_CFG		2#define SBID_PCI_DMA		3#define	SBID_SDRAM_SWAPPED	4#define SBID_ENUM		5#define SBID_REG_SDRAM		6#define SBID_REG_ILINE20	7#define SBID_REG_EMAC		8#define SBID_REG_CODEC		9#define SBID_REG_USB		10#define SBID_REG_PCI		11#define SBID_REG_MIPS		12#define SBID_REG_EXTIF		13#define	SBID_EXTIF		14#define	SBID_EJTAG		15#define	SBID_MAX		16static u32 ssb_get_addr(struct b44 *bp, u32 id, u32 instance){	switch (id) {	case SBID_PCI_DMA:		return 0x40000000;	case SBID_ENUM:		return 0x18000000;	case SBID_REG_EMAC:		return 0x18000000;	case SBID_REG_CODEC:		return 0x18001000;	case SBID_REG_PCI:		return 0x18002000;	default:		return 0;	};}static u32 ssb_get_core_rev(struct b44 *bp){	return (br32(bp, B44_SBIDHIGH) & SBIDHIGH_RC_MASK);}static u32 ssb_pci_setup(struct b44 *bp, u32 cores){	u32 bar_orig, pci_rev, val;	pci_read_config_dword(bp->pdev, SSB_BAR0_WIN, &bar_orig);	pci_write_config_dword(bp->pdev, SSB_BAR0_WIN,			       ssb_get_addr(bp, SBID_REG_PCI, 0));	pci_rev = ssb_get_core_rev(bp);	val = br32(bp, B44_SBINTVEC);	val |= cores;	bw32(bp, B44_SBINTVEC, val);	val = br32(bp, SSB_PCI_TRANS_2);	val |= SSB_PCI_PREF | SSB_PCI_BURST;	bw32(bp, SSB_PCI_TRANS_2, val);	pci_write_config_dword(bp->pdev, SSB_BAR0_WIN, bar_orig);	return pci_rev;}static void ssb_core_disable(struct b44 *bp){	if (br32(bp, B44_SBTMSLOW) & SBTMSLOW_RESET)		return;	bw32(bp, B44_SBTMSLOW, (SBTMSLOW_REJECT | SBTMSLOW_CLOCK));	b44_wait_bit(bp, B44_SBTMSLOW, SBTMSLOW_REJECT, 100000, 0);	b44_wait_bit(bp, B44_SBTMSHIGH, SBTMSHIGH_BUSY, 100000, 1);	bw32(bp, B44_SBTMSLOW, (SBTMSLOW_FGC | SBTMSLOW_CLOCK |			    SBTMSLOW_REJECT | SBTMSLOW_RESET));	br32(bp, B44_SBTMSLOW);	udelay(1);	bw32(bp, B44_SBTMSLOW, (SBTMSLOW_REJECT | SBTMSLOW_RESET));	br32(bp, B44_SBTMSLOW);	udelay(1);}static void ssb_core_reset(struct b44 *bp){	u32 val;	ssb_core_disable(bp);	bw32(bp, B44_SBTMSLOW, (SBTMSLOW_RESET | SBTMSLOW_CLOCK | SBTMSLOW_FGC));	br32(bp, B44_SBTMSLOW);	udelay(1);	/* Clear SERR if set, this is a hw bug workaround.  */	if (br32(bp, B44_SBTMSHIGH) & SBTMSHIGH_SERR)		bw32(bp, B44_SBTMSHIGH, 0);	val = br32(bp, B44_SBIMSTATE);	if (val & (SBIMSTATE_IBE | SBIMSTATE_TO))		bw32(bp, B44_SBIMSTATE, val & ~(SBIMSTATE_IBE | SBIMSTATE_TO));	bw32(bp, B44_SBTMSLOW, (SBTMSLOW_CLOCK | SBTMSLOW_FGC));	br32(bp, B44_SBTMSLOW);	udelay(1);	bw32(bp, B44_SBTMSLOW, (SBTMSLOW_CLOCK));	br32(bp, B44_SBTMSLOW);	udelay(1);}static int ssb_core_unit(struct b44 *bp){#if 0	u32 val = br32(bp, B44_SBADMATCH0);	u32 base;	type = val & SBADMATCH0_TYPE_MASK;	switch (type) {	case 0:		base = val & SBADMATCH0_BS0_MASK;		break;	case 1:		base = val & SBADMATCH0_BS1_MASK;		break;	case 2:	default:		base = val & SBADMATCH0_BS2_MASK;		break;	};#endif	return 0;}static int ssb_is_core_up(struct b44 *bp){	return ((br32(bp, B44_SBTMSLOW) & (SBTMSLOW_RESET | SBTMSLOW_REJECT | SBTMSLOW_CLOCK))		== SBTMSLOW_CLOCK);}static void __b44_cam_write(struct b44 *bp, unsigned char *data, int index){	u32 val;	val  = ((u32) data[2]) << 24;	val |= ((u32) data[3]) << 16;	val |= ((u32) data[4]) <<  8;	val |= ((u32) data[5]) <<  0;	bw32(bp, B44_CAM_DATA_LO, val);	val = (CAM_DATA_HI_VALID | 	       (((u32) data[0]) << 8) |	       (((u32) data[1]) << 0));	bw32(bp, B44_CAM_DATA_HI, val);	bw32(bp, B44_CAM_CTRL, (CAM_CTRL_WRITE |			    (index << CAM_CTRL_INDEX_SHIFT)));	b44_wait_bit(bp, B44_CAM_CTRL, CAM_CTRL_BUSY, 100, 1);	}static inline void __b44_disable_ints(struct b44 *bp){	bw32(bp, B44_IMASK, 0);}static void b44_disable_ints(struct b44 *bp){	__b44_disable_ints(bp);	/* Flush posted writes. */	br32(bp, B44_IMASK);}static void b44_enable_ints(struct b44 *bp){	bw32(bp, B44_IMASK, bp->imask);}static int b44_readphy(struct b44 *bp, int reg, u32 *val){	int err;	bw32(bp, B44_EMAC_ISTAT, EMAC_INT_MII);	bw32(bp, B44_MDIO_DATA, (MDIO_DATA_SB_START |			     (MDIO_OP_READ << MDIO_DATA_OP_SHIFT) |			     (bp->phy_addr << MDIO_DATA_PMD_SHIFT) |			     (reg << MDIO_DATA_RA_SHIFT) |			     (MDIO_TA_VALID << MDIO_DATA_TA_SHIFT)));	err = b44_wait_bit(bp, B44_EMAC_ISTAT, EMAC_INT_MII, 100, 0);	*val = br32(bp, B44_MDIO_DATA) & MDIO_DATA_DATA;	return err;}static int b44_writephy(struct b44 *bp, int reg, u32 val){	bw32(bp, B44_EMAC_ISTAT, EMAC_INT_MII);	bw32(bp, B44_MDIO_DATA, (MDIO_DATA_SB_START |			     (MDIO_OP_WRITE << MDIO_DATA_OP_SHIFT) |			     (bp->phy_addr << MDIO_DATA_PMD_SHIFT) |			     (reg << MDIO_DATA_RA_SHIFT) |			     (MDIO_TA_VALID << MDIO_DATA_TA_SHIFT) |			     (val & MDIO_DATA_DATA)));	return b44_wait_bit(bp, B44_EMAC_ISTAT, EMAC_INT_MII, 100, 0);}/* miilib interface *//* FIXME FIXME: phy_id is ignored, bp->phy_addr use is unconditional * due to code existing before miilib use was added to this driver. * Someone should remove this artificial driver limitation in * b44_{read,write}phy.  bp->phy_addr itself is fine (and needed). */static int b44_mii_read(struct net_device *dev, int phy_id, int location){	u32 val;	struct b44 *bp = netdev_priv(dev);	int rc = b44_readphy(bp, location, &val);	if (rc)		return 0xffffffff;	return val;}static void b44_mii_write(struct net_device *dev, int phy_id, int location,			 int val){	struct b44 *bp = netdev_priv(dev);	b44_writephy(bp, location, val);}static int b44_phy_reset(struct b44 *bp){	u32 val;	int err;	err = b44_writephy(bp, MII_BMCR, BMCR_RESET);	if (err)		return err;	udelay(100);	err = b44_readphy(bp, MII_BMCR, &val);	if (!err) {		if (val & BMCR_RESET) {			printk(KERN_ERR PFX "%s: PHY Reset would not complete.\n",			       bp->dev->name);			err = -ENODEV;		}	}	return 0;}static void __b44_set_flow_ctrl(struct b44 *bp, u32 pause_flags){	u32 val;	bp->flags &= ~(B44_FLAG_TX_PAUSE | B44_FLAG_RX_PAUSE);	bp->flags |= pause_flags;	val = br32(bp, B44_RXCONFIG);	if (pause_flags & B44_FLAG_RX_PAUSE)		val |= RXCONFIG_FLOW;	else		val &= ~RXCONFIG_FLOW;	bw32(bp, B44_RXCONFIG, val);	val = br32(bp, B44_MAC_FLOW);	if (pause_flags & B44_FLAG_TX_PAUSE)		val |= (MAC_FLOW_PAUSE_ENAB |			(0xc0 & MAC_FLOW_RX_HI_WATER));	else		val &= ~MAC_FLOW_PAUSE_ENAB;	bw32(bp, B44_MAC_FLOW, val);}static void b44_set_flow_ctrl(struct b44 *bp, u32 local, u32 remote){	u32 pause_enab = bp->flags & (B44_FLAG_TX_PAUSE |				      B44_FLAG_RX_PAUSE);	if (local & ADVERTISE_PAUSE_CAP) {		if (local & ADVERTISE_PAUSE_ASYM) {			if (remote & LPA_PAUSE_CAP)				pause_enab |= (B44_FLAG_TX_PAUSE |					       B44_FLAG_RX_PAUSE);			else if (remote & LPA_PAUSE_ASYM)				pause_enab |= B44_FLAG_RX_PAUSE;		} else {			if (remote & LPA_PAUSE_CAP)				pause_enab |= (B44_FLAG_TX_PAUSE |					       B44_FLAG_RX_PAUSE);		}	} else if (local & ADVERTISE_PAUSE_ASYM) {		if ((remote & LPA_PAUSE_CAP) &&		    (remote & LPA_PAUSE_ASYM))			pause_enab |= B44_FLAG_TX_PAUSE;	}	__b44_set_flow_ctrl(bp, pause_enab);}static int b44_setup_phy(struct b44 *bp){	u32 val;	int err;	if ((err = b44_readphy(bp, B44_MII_ALEDCTRL, &val)) != 0)		goto out;	if ((err = b44_writephy(bp, B44_MII_ALEDCTRL,				val & MII_ALEDCTRL_ALLMSK)) != 0)		goto out;	if ((err = b44_readphy(bp, B44_MII_TLEDCTRL, &val)) != 0)		goto out;	if ((err = b44_writephy(bp, B44_MII_TLEDCTRL,				val | MII_TLEDCTRL_ENABLE)) != 0)		goto out;	if (!(bp->flags & B44_FLAG_FORCE_LINK)) {		u32 adv = ADVERTISE_CSMA;		if (bp->flags & B44_FLAG_ADV_10HALF)			adv |= ADVERTISE_10HALF;		if (bp->flags & B44_FLAG_ADV_10FULL)			adv |= ADVERTISE_10FULL;		if (bp->flags & B44_FLAG_ADV_100HALF)			adv |= ADVERTISE_100HALF;		if (bp->flags & B44_FLAG_ADV_100FULL)			adv |= ADVERTISE_100FULL;		if (bp->flags & B44_FLAG_PAUSE_AUTO)			adv |= ADVERTISE_PAUSE_CAP | ADVERTISE_PAUSE_ASYM;		if ((err = b44_writephy(bp, MII_ADVERTISE, adv)) != 0)			goto out;		if ((err = b44_writephy(bp, MII_BMCR, (BMCR_ANENABLE |						       BMCR_ANRESTART))) != 0)			goto out;	} else {		u32 bmcr;		if ((err = b44_readphy(bp, MII_BMCR, &bmcr)) != 0)			goto out;		bmcr &= ~(BMCR_FULLDPLX | BMCR_ANENABLE | BMCR_SPEED100);		if (bp->flags & B44_FLAG_100_BASE_T)			bmcr |= BMCR_SPEED100;		if (bp->flags & B44_FLAG_FULL_DUPLEX)			bmcr |= BMCR_FULLDPLX;		if ((err = b44_writephy(bp, MII_BMCR, bmcr)) != 0)			goto out;

⌨️ 快捷键说明

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