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

📄 sunhme.c

📁 powerpc内核mpc8241linux系统下net驱动程序
💻 C
📖 第 1 页 / 共 5 页
字号:
/* sunhme.c: Sparc HME/BigMac 10/100baseT half/full duplex auto switching, *           auto carrier detecting ethernet driver.  Also known as the *           "Happy Meal Ethernet" found on SunSwift SBUS cards. * * Copyright (C) 1996, 1998 David S. Miller (davem@caipfs.rutgers.edu) */static char *version =        "sunhme.c:v1.10 27/Jan/99 David S. Miller (davem@caipfs.rutgers.edu)\n";#include <linux/module.h>#include <linux/config.h>#include <linux/kernel.h>#include <linux/sched.h>#include <linux/types.h>#include <linux/fcntl.h>#include <linux/interrupt.h>#include <linux/ptrace.h>#include <linux/ioport.h>#include <linux/in.h>#include <linux/malloc.h>#include <linux/string.h>#include <linux/delay.h>#include <linux/init.h>#include <asm/system.h>#include <asm/bitops.h>#include <asm/io.h>#include <asm/dma.h>#include <linux/errno.h>#include <asm/byteorder.h>#include <asm/idprom.h>#include <asm/sbus.h>#include <asm/openprom.h>#include <asm/oplib.h>#include <asm/auxio.h>#include <asm/pgtable.h>#include <asm/irq.h>#ifndef __sparc_v9__#include <asm/io-unit.h>#endif#include <asm/ethtool.h>#include <asm/uaccess.h>#include <linux/netdevice.h>#include <linux/etherdevice.h>#include <linux/skbuff.h>#ifdef CONFIG_PCI#include <linux/pci.h>#include <asm/pbm.h>#endif#include "sunhme.h"#ifdef MODULEstatic struct happy_meal *root_happy_dev = NULL;#endifstatic struct quattro *qfe_sbus_list = NULL;#ifdef CONFIG_PCIstatic struct quattro *qfe_pci_list = NULL;#endif#undef HMEDEBUG#undef SXDEBUG#undef RXDEBUG#undef TXDEBUG#undef TXLOGGING#ifdef TXLOGGINGstruct hme_tx_logent {	unsigned int tstamp;	int tx_new, tx_old;	unsigned int action;#define TXLOG_ACTION_IRQ	0x01#define TXLOG_ACTION_TXMIT	0x02#define TXLOG_ACTION_TBUSY	0x04#define TXLOG_ACTION_NBUFS	0x08	unsigned int status;};#define TX_LOG_LEN	128static struct hme_tx_logent tx_log[TX_LOG_LEN];static int txlog_cur_entry = 0;static __inline__ void tx_add_log(struct happy_meal *hp, unsigned int a, unsigned int s){	struct hme_tx_logent *tlp;	unsigned long flags;	save_and_cli(flags);	tlp = &tx_log[txlog_cur_entry];	tlp->tstamp = (unsigned int)jiffies;	tlp->tx_new = hp->tx_new;	tlp->tx_old = hp->tx_old;	tlp->action = a;	tlp->status = s;	txlog_cur_entry = (txlog_cur_entry + 1) & (TX_LOG_LEN - 1);	restore_flags(flags);}static __inline__ void tx_dump_log(void){	int i, this;	this = txlog_cur_entry;	for(i = 0; i < TX_LOG_LEN; i++) {		printk("TXLOG[%d]: j[%08x] tx[N(%d)O(%d)] action[%08x] stat[%08x]\n", i,		       tx_log[this].tstamp,		       tx_log[this].tx_new, tx_log[this].tx_old,		       tx_log[this].action, tx_log[this].status);		this = (this + 1) & (TX_LOG_LEN - 1);	}}static __inline__ void tx_dump_ring(struct happy_meal *hp){	struct hmeal_init_block *hb = hp->happy_block;	struct happy_meal_txd *tp = &hb->happy_meal_txd[0];	int i;	for(i = 0; i < TX_RING_SIZE; i+=4) {		printk("TXD[%d..%d]: [%08x:%08x] [%08x:%08x] [%08x:%08x] [%08x:%08x]\n",		       i, i + 4,		       le32_to_cpu(tp[i].tx_flags), le32_to_cpu(tp[i].tx_addr),		       le32_to_cpu(tp[i + 1].tx_flags), le32_to_cpu(tp[i + 1].tx_addr),		       le32_to_cpu(tp[i + 2].tx_flags), le32_to_cpu(tp[i + 2].tx_addr),		       le32_to_cpu(tp[i + 3].tx_flags), le32_to_cpu(tp[i + 3].tx_addr));	}}#else#define tx_add_log(hp, a, s)		do { } while(0)#define tx_dump_log()			do { } while(0)#define tx_dump_ring(hp)		do { } while(0)#endif#ifdef HMEDEBUG#define HMD(x)  printk x#else#define HMD(x)#endif/* #define AUTO_SWITCH_DEBUG */#ifdef AUTO_SWITCH_DEBUG#define ASD(x)  printk x#else#define ASD(x)#endif#define DEFAULT_IPG0      16 /* For lance-mode only */#define DEFAULT_IPG1       8 /* For all modes */#define DEFAULT_IPG2       4 /* For all modes */#define DEFAULT_JAMSIZE    4 /* Toe jam *//* Oh yes, the MIF BitBang is mighty fun to program.  BitBucket is more like it. */#define BB_PUT_BIT(hp, tregs, bit)						\do {	hme_write32(hp, &(tregs)->bb_data, (bit));				\	hme_write32(hp, &(tregs)->bb_clock, 0);					\	hme_write32(hp, &(tregs)->bb_clock, 1);					\} while(0)#define BB_GET_BIT(hp, tregs, internal)						\({										\	hme_write32(hp, &(tregs)->bb_clock, 0);					\	hme_write32(hp, &(tregs)->bb_clock, 1);					\	if(internal)								\		hme_read32(hp, &(tregs)->cfg) & TCV_CFG_MDIO0;			\	else									\		hme_read32(hp, &(tregs)->cfg) & TCV_CFG_MDIO1;			\})#define BB_GET_BIT2(hp, tregs, internal)					\({										\	int retval;								\	hme_write32(hp, &(tregs)->bb_clock, 0);					\	udelay(1);								\	if(internal)								\		retval = hme_read32(hp, &(tregs)->cfg) & TCV_CFG_MDIO0;		\	else									\		retval = hme_read32(hp, &(tregs)->cfg) & TCV_CFG_MDIO1;		\	hme_write32(hp, &(tregs)->bb_clock, 1);					\	retval;									\})#define TCVR_FAILURE      0x80000000     /* Impossible MIF read value */static inline int happy_meal_bb_read(struct happy_meal *hp,				     struct hmeal_tcvregs *tregs, int reg){	volatile int unused;	unsigned long tmp;	int retval = 0;	int i;	ASD(("happy_meal_bb_read: reg=%d ", reg));	/* Enable the MIF BitBang outputs. */	hme_write32(hp, &tregs->bb_oenab, 1);	/* Force BitBang into the idle state. */	for(i = 0; i < 32; i++)		BB_PUT_BIT(hp, tregs, 1);	/* Give it the read sequence. */	BB_PUT_BIT(hp, tregs, 0);	BB_PUT_BIT(hp, tregs, 1);	BB_PUT_BIT(hp, tregs, 1);	BB_PUT_BIT(hp, tregs, 0);	/* Give it the PHY address. */	tmp = hp->paddr & 0xff;	for(i = 4; i >= 0; i--)		BB_PUT_BIT(hp, tregs, ((tmp >> i) & 1));	/* Tell it what register we want to read. */	tmp = (reg & 0xff);	for(i = 4; i >= 0; i--)		BB_PUT_BIT(hp, tregs, ((tmp >> i) & 1));	/* Close down the MIF BitBang outputs. */	hme_write32(hp, &tregs->bb_oenab, 0);	/* Now read in the value. */	unused = BB_GET_BIT2(hp, tregs, (hp->tcvr_type == internal));	for(i = 15; i >= 0; i--)		retval |= BB_GET_BIT2(hp, tregs, (hp->tcvr_type == internal));	unused = BB_GET_BIT2(hp, tregs, (hp->tcvr_type == internal));	unused = BB_GET_BIT2(hp, tregs, (hp->tcvr_type == internal));	unused = BB_GET_BIT2(hp, tregs, (hp->tcvr_type == internal));	ASD(("value=%x\n", retval));	return retval;}static inline void happy_meal_bb_write(struct happy_meal *hp,				       struct hmeal_tcvregs *tregs, int reg,				       unsigned short value){	unsigned long tmp;	int i;	ASD(("happy_meal_bb_write: reg=%d value=%x\n", reg, value));	/* Enable the MIF BitBang outputs. */	hme_write32(hp, &tregs->bb_oenab, 1);	/* Force BitBang into the idle state. */	for(i = 0; i < 32; i++)		BB_PUT_BIT(hp, tregs, 1);	/* Give it write sequence. */	BB_PUT_BIT(hp, tregs, 0);	BB_PUT_BIT(hp, tregs, 1);	BB_PUT_BIT(hp, tregs, 0);	BB_PUT_BIT(hp, tregs, 1);	/* Give it the PHY address. */	tmp = (hp->paddr & 0xff);	for(i = 4; i >= 0; i--)		BB_PUT_BIT(hp, tregs, ((tmp >> i) & 1));	/* Tell it what register we will be writing. */	tmp = (reg & 0xff);	for(i = 4; i >= 0; i--)		BB_PUT_BIT(hp, tregs, ((tmp >> i) & 1));	/* Tell it to become ready for the bits. */	BB_PUT_BIT(hp, tregs, 1);	BB_PUT_BIT(hp, tregs, 0);	for(i = 15; i >= 0; i--)		BB_PUT_BIT(hp, tregs, ((value >> i) & 1));	/* Close down the MIF BitBang outputs. */	hme_write32(hp, &tregs->bb_oenab, 0);}#define TCVR_READ_TRIES   16static inline int happy_meal_tcvr_read(struct happy_meal *hp,				       struct hmeal_tcvregs *tregs, int reg){	int tries = TCVR_READ_TRIES;	int retval;	ASD(("happy_meal_tcvr_read: reg=0x%02x ", reg));	if(hp->tcvr_type == none) {		ASD(("no transceiver, value=TCVR_FAILURE\n"));		return TCVR_FAILURE;	}	if(!(hp->happy_flags & HFLAG_FENABLE)) {		ASD(("doing bit bang\n"));		return happy_meal_bb_read(hp, tregs, reg);	}	hme_write32(hp, &tregs->frame,		    (FRAME_READ | (hp->paddr << 23) | ((reg & 0xff) << 18)));	while(!(hme_read32(hp, &tregs->frame) & 0x10000) && --tries)		udelay(20);	if(!tries) {		printk("happy meal: Aieee, transceiver MIF read bolixed\n");		return TCVR_FAILURE;	}	retval = hme_read32(hp, &tregs->frame) & 0xffff;	ASD(("value=%04x\n", retval));	return retval;}#define TCVR_WRITE_TRIES  16static inline void happy_meal_tcvr_write(struct happy_meal *hp,					 struct hmeal_tcvregs *tregs, int reg,					 unsigned short value){	int tries = TCVR_WRITE_TRIES;		ASD(("happy_meal_tcvr_write: reg=0x%02x value=%04x\n", reg, value));	/* Welcome to Sun Microsystems, can I take your order please? */	if(!hp->happy_flags & HFLAG_FENABLE)		return happy_meal_bb_write(hp, tregs, reg, value);	/* Would you like fries with that? */	hme_write32(hp, &tregs->frame,		    (FRAME_WRITE | (hp->paddr << 23) |		     ((reg & 0xff) << 18) | (value & 0xffff)));	while(!(hme_read32(hp, &tregs->frame) & 0x10000) && --tries)		udelay(20);	/* Anything else? */	if(!tries)		printk("happy meal: Aieee, transceiver MIF write bolixed\n");	/* Fifty-two cents is your change, have a nice day. */}/* Auto negotiation.  The scheme is very simple.  We have a timer routine * that keeps watching the auto negotiation process as it progresses. * The DP83840 is first told to start doing it's thing, we set up the time * and place the timer state machine in it's initial state. * * Here the timer peeks at the DP83840 status registers at each click to see * if the auto negotiation has completed, we assume here that the DP83840 PHY * will time out at some point and just tell us what (didn't) happen.  For * complete coverage we only allow so many of the ticks at this level to run, * when this has expired we print a warning message and try another strategy. * This "other" strategy is to force the interface into various speed/duplex * configurations and we stop when we see a link-up condition before the * maximum number of "peek" ticks have occurred. * * Once a valid link status has been detected we configure the BigMAC and * the rest of the Happy Meal to speak the most efficient protocol we could * get a clean link for.  The priority for link configurations, highest first * is: *                 100 Base-T Full Duplex *                 100 Base-T Half Duplex *                 10 Base-T Full Duplex *                 10 Base-T Half Duplex * * We start a new timer now, after a successful auto negotiation status has * been detected.  This timer just waits for the link-up bit to get set in * the BMCR of the DP83840.  When this occurs we print a kernel log message * describing the link type in use and the fact that it is up. * * If a fatal error of some sort is signalled and detected in the interrupt * service routine, and the chip is reset, or the link is ifconfig'd down * and then back up, this entire process repeats itself all over again. */static int try_next_permutation(struct happy_meal *hp, struct hmeal_tcvregs *tregs){	hp->sw_bmcr = happy_meal_tcvr_read(hp, tregs, DP83840_BMCR);	/* Downgrade from full to half duplex.  Only possible	 * via ethtool.	 */	if(hp->sw_bmcr & BMCR_FULLDPLX) {		hp->sw_bmcr &= ~(BMCR_FULLDPLX);		happy_meal_tcvr_write(hp, tregs, DP83840_BMCR, hp->sw_bmcr);		return 0;	}	/* Downgrade from 100 to 10. */	if(hp->sw_bmcr & BMCR_SPEED100) {		hp->sw_bmcr &= ~(BMCR_SPEED100);		happy_meal_tcvr_write(hp, tregs, DP83840_BMCR, hp->sw_bmcr);		return 0;	}	/* We've tried everything. */	return -1;}static void display_link_mode(struct happy_meal *hp, struct hmeal_tcvregs *tregs){	printk("%s: Link is up using ", hp->dev->name);	if(hp->tcvr_type == external)		printk("external ");	else		printk("internal ");	printk("transceiver at ");	hp->sw_lpa = happy_meal_tcvr_read(hp, tregs, DP83840_LPA);	if(hp->sw_lpa & (LPA_100HALF | LPA_100FULL)) {		if(hp->sw_lpa & LPA_100FULL)			printk("100Mb/s, Full Duplex.\n");		else			printk("100Mb/s, Half Duplex.\n");	} else {		if(hp->sw_lpa & LPA_10FULL)			printk("10Mb/s, Full Duplex.\n");		else			printk("10Mb/s, Half Duplex.\n");	}}static void display_forced_link_mode(struct happy_meal *hp, struct hmeal_tcvregs *tregs){	printk("%s: Link has been forced up using ", hp->dev->name);	if(hp->tcvr_type == external)		printk("external ");	else		printk("internal ");	printk("transceiver at ");	hp->sw_bmcr = happy_meal_tcvr_read(hp, tregs, DP83840_BMCR);	if(hp->sw_bmcr & BMCR_SPEED100)		printk("100Mb/s, ");	else		printk("10Mb/s, ");	if(hp->sw_bmcr & BMCR_FULLDPLX)		printk("Full Duplex.\n");	else		printk("Half Duplex.\n");}static int set_happy_link_modes(struct happy_meal *hp, struct hmeal_tcvregs *tregs){	int full;	/* All we care about is making sure the bigmac tx_cfg has a	 * proper duplex setting.	 */	if(hp->timer_state == arbwait) {		hp->sw_lpa = happy_meal_tcvr_read(hp, tregs, DP83840_LPA);		if(!(hp->sw_lpa & (LPA_10HALF | LPA_10FULL | LPA_100HALF | LPA_100FULL)))			goto no_response;		if(hp->sw_lpa & LPA_100FULL)			full = 1;		else if(hp->sw_lpa & LPA_100HALF)			full = 0;		else if(hp->sw_lpa & LPA_10FULL)			full = 1;		else			full = 0;	} else {		/* Forcing a link mode. */		hp->sw_bmcr = happy_meal_tcvr_read(hp, tregs, DP83840_BMCR);		if(hp->sw_bmcr & BMCR_FULLDPLX)			full = 1;		else

⌨️ 快捷键说明

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