📄 sunhme.c
字号:
/* 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 + -