📄 sunbmac.c
字号:
/* sunbmac.c: Driver for Sparc BigMAC 100baseT ethernet adapters. * * Copyright (C) 1997, 1998 David S. Miller (davem@caip.rutgers.edu) */static char *version = "sunbmac.c:v1.1 8/Dec/98 David S. Miller (davem@caipfs.rutgers.edu)\n";#include <linux/module.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/system.h>#include <asm/pgtable.h>#include <linux/netdevice.h>#include <linux/etherdevice.h>#include <linux/skbuff.h>#include "sunbmac.h"#undef DEBUG_PROBE#undef DEBUG_TX#undef DEBUG_IRQ#ifdef DEBUG_PROBE#define DP(x) printk x#else#define DP(x)#endif#ifdef DEBUG_TX#define DTX(x) printk x#else#define DTX(x)#endif#ifdef DEBUG_IRQ#define DIRQ(x) printk x#else#define DIRQ(x)#endif#ifdef MODULEstatic struct bigmac *root_bigmac_dev = NULL;#endif#define DEFAULT_JAMSIZE 4 /* Toe jam */#define QEC_RESET_TRIES 200static inline int qec_global_reset(struct qe_globreg *gregs){ int tries = QEC_RESET_TRIES; gregs->ctrl = GLOB_CTRL_RESET; while(--tries) { if(gregs->ctrl & GLOB_CTRL_RESET) { udelay(20); continue; } break; } if(tries) return 0; printk("BigMAC: Cannot reset the QEC.\n"); return -1;}static void qec_init(struct bigmac *bp){ struct qe_globreg *gregs = bp->gregs; struct linux_sbus_device *qec_sdev = bp->qec_sbus_dev; unsigned char bsizes = bp->bigmac_bursts; unsigned int regval; /* 64byte bursts do not work at the moment, do * not even try to enable them. -DaveM */ if(bsizes & DMA_BURST32) regval = GLOB_CTRL_B32; else regval = GLOB_CTRL_B16; gregs->ctrl = regval | GLOB_CTRL_BMODE; gregs->psize = GLOB_PSIZE_2048; /* All of memsize is given to bigmac. */ gregs->msize = qec_sdev->reg_addrs[1].reg_size; /* Half to the transmitter, half to the receiver. */ gregs->rsize = gregs->tsize = qec_sdev->reg_addrs[1].reg_size >> 1;}/* XXX auto negotiation on these things might not be pleasant... */#define TX_RESET_TRIES 32#define RX_RESET_TRIES 32static inline void bigmac_tx_reset(struct BIG_MAC_regs *bregs){ int tries = TX_RESET_TRIES; bregs->tx_cfg = 0; /* The fifo threshold bit is read-only and does * not clear. -DaveM */ while((bregs->tx_cfg & ~(BIGMAC_TXCFG_FIFO)) != 0 && --tries != 0) udelay(20); if(!tries) { printk("BIGMAC: Transmitter will not reset.\n"); printk("BIGMAC: tx_cfg is %08x\n", bregs->tx_cfg); }}static inline void bigmac_rx_reset(struct BIG_MAC_regs *bregs){ int tries = RX_RESET_TRIES; bregs->rx_cfg = 0; while((bregs->rx_cfg) && --tries) udelay(20); if(!tries) { printk("BIGMAC: Receiver will not reset.\n"); printk("BIGMAC: rx_cfg is %08x\n", bregs->rx_cfg); }}/* Reset the transmitter and receiver. */static void bigmac_stop(struct bigmac *bp){ bigmac_tx_reset(bp->bregs); bigmac_rx_reset(bp->bregs);}static void bigmac_get_counters(struct bigmac *bp, struct BIG_MAC_regs *bregs){ struct enet_statistics *stats = &bp->enet_stats; stats->rx_crc_errors += bregs->rcrce_ctr; bregs->rcrce_ctr = 0; stats->rx_frame_errors += bregs->unale_ctr; bregs->unale_ctr = 0; stats->rx_length_errors += bregs->gle_ctr; bregs->gle_ctr = 0; stats->tx_aborted_errors += bregs->ex_ctr; stats->collisions += (bregs->ex_ctr + bregs->lt_ctr); bregs->ex_ctr = bregs->lt_ctr = 0;}static inline void bigmac_clean_rings(struct bigmac *bp){ int i; for(i = 0; i < RX_RING_SIZE; i++) { if(bp->rx_skbs[i] != NULL) { dev_kfree_skb(bp->rx_skbs[i]); bp->rx_skbs[i] = NULL; } } for(i = 0; i < TX_RING_SIZE; i++) { if(bp->tx_skbs[i] != NULL) { dev_kfree_skb(bp->tx_skbs[i]); bp->tx_skbs[i] = NULL; } }}static void bigmac_init_rings(struct bigmac *bp, int from_irq){ struct bmac_init_block *bb = bp->bmac_block; struct device *dev = bp->dev; int i, gfp_flags = GFP_KERNEL; if(from_irq || in_interrupt()) gfp_flags = GFP_ATOMIC; bp->rx_new = bp->rx_old = bp->tx_new = bp->tx_old = 0; /* Free any skippy bufs left around in the rings. */ bigmac_clean_rings(bp); /* Now get new skippy bufs for the receive ring. */ for(i = 0; i < RX_RING_SIZE; i++) { struct sk_buff *skb; skb = big_mac_alloc_skb(RX_BUF_ALLOC_SIZE, gfp_flags); if(!skb) continue; bp->rx_skbs[i] = skb; skb->dev = dev; /* Because we reserve afterwards. */ skb_put(skb, ETH_FRAME_LEN); skb_reserve(skb, 34); bb->be_rxd[i].rx_addr = sbus_dvma_addr(skb->data); bb->be_rxd[i].rx_flags = (RXD_OWN | ((RX_BUF_ALLOC_SIZE - 34) & RXD_LENGTH)); } for(i = 0; i < TX_RING_SIZE; i++) bb->be_txd[i].tx_flags = bb->be_txd[i].tx_addr = 0;}#ifndef __sparc_v9__static void sun4c_bigmac_init_rings(struct bigmac *bp){ struct bmac_init_block *bb = bp->bmac_block; __u32 bbufs_dvma = bp->s4c_buf_dvma; int i; bp->rx_new = bp->rx_old = bp->tx_new = bp->tx_old = 0; for(i = 0; i < RX_RING_SIZE; i++) { bb->be_rxd[i].rx_addr = bbufs_dvma + bbuf_offset(rx_buf, i); bb->be_rxd[i].rx_flags = (RXD_OWN | (SUN4C_RX_BUFF_SIZE & RXD_LENGTH)); } for(i = 0; i < TX_RING_SIZE; i++) bb->be_txd[i].tx_flags = bb->be_txd[i].tx_addr = 0;}#endif#define MGMT_CLKON (MGMT_PAL_INT_MDIO|MGMT_PAL_EXT_MDIO|MGMT_PAL_OENAB|MGMT_PAL_DCLOCK)#define MGMT_CLKOFF (MGMT_PAL_INT_MDIO|MGMT_PAL_EXT_MDIO|MGMT_PAL_OENAB)static inline void idle_transceiver(struct bmac_tcvr *tregs){ volatile unsigned int garbage; int i = 20; while(i--) { tregs->mgmt_pal = MGMT_CLKOFF; garbage = tregs->mgmt_pal; tregs->mgmt_pal = MGMT_CLKON; garbage = tregs->mgmt_pal; }}static void write_tcvr_bit(struct bigmac *bp, struct bmac_tcvr *tregs, int bit){ volatile unsigned int garbage; if(bp->tcvr_type == internal) { bit = (bit & 1) << 3; tregs->mgmt_pal = bit | (MGMT_PAL_OENAB | MGMT_PAL_EXT_MDIO); garbage = tregs->mgmt_pal; tregs->mgmt_pal = bit | (MGMT_PAL_OENAB | MGMT_PAL_EXT_MDIO | MGMT_PAL_DCLOCK); garbage = tregs->mgmt_pal; } else if(bp->tcvr_type == external) { bit = (bit & 1) << 2; tregs->mgmt_pal = bit | (MGMT_PAL_INT_MDIO | MGMT_PAL_OENAB); garbage = tregs->mgmt_pal; tregs->mgmt_pal = bit | (MGMT_PAL_INT_MDIO | MGMT_PAL_OENAB | MGMT_PAL_DCLOCK); garbage = tregs->mgmt_pal; } else { printk("write_tcvr_bit: No transceiver type known!\n"); }}static int read_tcvr_bit(struct bigmac *bp, struct bmac_tcvr *tregs){ volatile unsigned int garbage; int retval = 0; if(bp->tcvr_type == internal) { tregs->mgmt_pal = MGMT_PAL_EXT_MDIO; garbage = tregs->mgmt_pal; tregs->mgmt_pal = MGMT_PAL_EXT_MDIO | MGMT_PAL_DCLOCK; garbage = tregs->mgmt_pal; retval = (tregs->mgmt_pal & MGMT_PAL_INT_MDIO) >> 3; } else if(bp->tcvr_type == external) { tregs->mgmt_pal = MGMT_PAL_INT_MDIO; garbage = tregs->mgmt_pal; tregs->mgmt_pal = MGMT_PAL_INT_MDIO | MGMT_PAL_DCLOCK; garbage = tregs->mgmt_pal; retval = (tregs->mgmt_pal & MGMT_PAL_EXT_MDIO) >> 2; } else { printk("read_tcvr_bit: No transceiver type known!\n"); } return retval;}static int read_tcvr_bit2(struct bigmac *bp, struct bmac_tcvr *tregs){ volatile unsigned int garbage; int retval = 0; if(bp->tcvr_type == internal) { tregs->mgmt_pal = MGMT_PAL_EXT_MDIO; garbage = tregs->mgmt_pal; retval = (tregs->mgmt_pal & MGMT_PAL_INT_MDIO) >> 3; tregs->mgmt_pal = (MGMT_PAL_EXT_MDIO | MGMT_PAL_DCLOCK); garbage = tregs->mgmt_pal; } else if(bp->tcvr_type == external) { tregs->mgmt_pal = MGMT_PAL_INT_MDIO; garbage = tregs->mgmt_pal; retval = (tregs->mgmt_pal & MGMT_PAL_EXT_MDIO) >> 2; tregs->mgmt_pal = (MGMT_PAL_INT_MDIO | MGMT_PAL_DCLOCK); garbage = tregs->mgmt_pal; } else { printk("read_tcvr_bit2: No transceiver type known!\n"); } return retval;}static inline void put_tcvr_byte(struct bigmac *bp, struct bmac_tcvr *tregs, unsigned int byte){ int shift = 4; do { write_tcvr_bit(bp, tregs, ((byte >> shift) & 1)); shift -= 1; } while (shift >= 0);}static void bigmac_tcvr_write(struct bigmac *bp, struct bmac_tcvr *tregs, int reg, unsigned short val){ int shift; reg &= 0xff; val &= 0xffff; switch(bp->tcvr_type) { case internal: case external: break; default: printk("bigmac_tcvr_read: Whoops, no known transceiver type.\n"); return; }; idle_transceiver(tregs); write_tcvr_bit(bp, tregs, 0); write_tcvr_bit(bp, tregs, 1); write_tcvr_bit(bp, tregs, 0); write_tcvr_bit(bp, tregs, 1); put_tcvr_byte(bp, tregs, ((bp->tcvr_type == internal) ? BIGMAC_PHY_INTERNAL : BIGMAC_PHY_EXTERNAL)); put_tcvr_byte(bp, tregs, reg); write_tcvr_bit(bp, tregs, 1); write_tcvr_bit(bp, tregs, 0); shift = 15; do { write_tcvr_bit(bp, tregs, (val >> shift) & 1); shift -= 1; } while(shift >= 0);}static unsigned short bigmac_tcvr_read(struct bigmac *bp, struct bmac_tcvr *tregs, int reg){ unsigned short retval = 0; reg &= 0xff; switch(bp->tcvr_type) { case internal: case external: break; default: printk("bigmac_tcvr_read: Whoops, no known transceiver type.\n"); return 0xffff; }; idle_transceiver(tregs); write_tcvr_bit(bp, tregs, 0); write_tcvr_bit(bp, tregs, 1); write_tcvr_bit(bp, tregs, 1); write_tcvr_bit(bp, tregs, 0); put_tcvr_byte(bp, tregs, ((bp->tcvr_type == internal) ? BIGMAC_PHY_INTERNAL : BIGMAC_PHY_EXTERNAL)); put_tcvr_byte(bp, tregs, reg); if(bp->tcvr_type == external) { int shift = 15; (void) read_tcvr_bit2(bp, tregs); (void) read_tcvr_bit2(bp, tregs); do { int tmp; tmp = read_tcvr_bit2(bp, tregs); retval |= ((tmp & 1) << shift); shift -= 1; } while(shift >= 0); (void) read_tcvr_bit2(bp, tregs); (void) read_tcvr_bit2(bp, tregs); (void) read_tcvr_bit2(bp, tregs); } else { int shift = 15; (void) read_tcvr_bit(bp, tregs); (void) read_tcvr_bit(bp, tregs); do { int tmp; tmp = read_tcvr_bit(bp, tregs); retval |= ((tmp & 1) << shift); shift -= 1; } while(shift >= 0); (void) read_tcvr_bit(bp, tregs); (void) read_tcvr_bit(bp, tregs); (void) read_tcvr_bit(bp, tregs); } return retval;}static void bigmac_tcvr_init(struct bigmac *bp){ volatile unsigned int garbage; struct bmac_tcvr *tregs = bp->tregs; idle_transceiver(tregs); tregs->mgmt_pal = (MGMT_PAL_INT_MDIO | MGMT_PAL_EXT_MDIO | MGMT_PAL_DCLOCK); garbage = tregs->mgmt_pal; /* Only the bit for the present transceiver (internal or * external) will stick, set them both and see what stays. */ tregs->mgmt_pal = (MGMT_PAL_INT_MDIO | MGMT_PAL_EXT_MDIO); garbage = tregs->mgmt_pal; udelay(20); if(tregs->mgmt_pal & MGMT_PAL_EXT_MDIO) { bp->tcvr_type = external; tregs->tcvr_pal = ~(TCVR_PAL_EXTLBACK | TCVR_PAL_MSENSE | TCVR_PAL_LTENABLE); garbage = tregs->tcvr_pal; } else if(tregs->mgmt_pal & MGMT_PAL_INT_MDIO) { bp->tcvr_type = internal; tregs->tcvr_pal = ~(TCVR_PAL_SERIAL | TCVR_PAL_EXTLBACK | TCVR_PAL_MSENSE | TCVR_PAL_LTENABLE); garbage = tregs->tcvr_pal; } else { printk("BIGMAC: AIEEE, neither internal nor " "external MDIO available!\n"); printk("BIGMAC: mgmt_pal[%08x] tcvr_pal[%08x]\n", tregs->mgmt_pal, tregs->tcvr_pal); }}static int bigmac_init(struct bigmac *, int);static int try_next_permutation(struct bigmac *bp, struct bmac_tcvr *tregs){ if(bp->sw_bmcr & BMCR_SPEED100) { int timeout; /* Reset the PHY. */ bp->sw_bmcr = (BMCR_ISOLATE | BMCR_PDOWN | BMCR_LOOPBACK); bigmac_tcvr_write(bp, tregs, BIGMAC_BMCR, bp->sw_bmcr); bp->sw_bmcr = (BMCR_RESET); bigmac_tcvr_write(bp, tregs, BIGMAC_BMCR, bp->sw_bmcr); timeout = 64; while(--timeout) { bp->sw_bmcr = bigmac_tcvr_read(bp, tregs, BIGMAC_BMCR); if((bp->sw_bmcr & BMCR_RESET) == 0) break;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -