📄 dev_sb_mac.c
字号:
linkspeed = ETHER_SPEED_UNKNOWN; xprintf("%s: Link speed: ", bcm4401_devname(sc)); if (sc->phy_addr == 0x1E) { /* XXX for SOC parts, this address may indicate a Roboswitch. */ xprintf("100BaseT FDX (switch)\n"); linkspeed = ETHER_SPEED_100FDX; } else { /* Read twice to clear latching bits */ status = mii_read(sc, MII_BMSR); status = mii_read(sc, MII_BMSR); if ((status & (BMSR_AUTONEG | BMSR_LINKSTAT)) == (BMSR_AUTONEG | BMSR_LINKSTAT)) control = mii_read(sc, MII_BMCR); else { for (timeout = 4*CFE_HZ; timeout > 0; timeout -= CFE_HZ/2) { status = mii_read(sc, MII_BMSR); if ((status & BMSR_ANCOMPLETE) != 0) break; cfe_sleep(CFE_HZ/2); } } remote = mii_read(sc, MII_ANLPAR); if ((status & BMSR_ANCOMPLETE) != 0) { /* A link partner was negotiated... */ if ((remote & ANLPAR_TXFD) != 0) { xprintf("100BaseT FDX\n"); linkspeed = ETHER_SPEED_100FDX; } else if ((remote & ANLPAR_TXHD) != 0) { xprintf("100BaseT HDX\n"); linkspeed = ETHER_SPEED_100HDX; } else if ((remote & ANLPAR_10FD) != 0) { xprintf("10BaseT FDX\n"); linkspeed = ETHER_SPEED_10FDX; } else if ((remote & ANLPAR_10HD) != 0) { xprintf("10BaseT HDX\n"); linkspeed = ETHER_SPEED_10HDX; } } else { /* no link partner convergence */ xprintf("Unknown\n"); linkspeed = ETHER_SPEED_UNKNOWN; } sc->linkspeed = linkspeed; /* clear latching bits */ status = mii_read(sc, MII_BMSR); } mii_dump(sc, "final PHY");}static intbcm4401_reset(bcm4401_softc *sc){ return -1;}/* SPROM access routines. Random read access to the SPROM will produce a bus error due to PCI timeouts. As an apparent (undocumented) side effect, the requested word will be fetched to a local buffer so that the next access will succeed. */#define SPROM_SIZE 0x80static intsprom_read_all(bcm4401_softc *sc, uint8_t dest[]){ int i; uint32_t t; jmpbuf_t *jb; jb = exc_initialize_block(); if (jb == NULL) return -1; for (i = 0; i < SPROM_SIZE; i += 4) { if (exc_try(jb) == 0) { /* On pass 2 parts, the following read gets a bus error */ t = READCSR(sc, SPROM_BASE + i); cfe_usleep(1000); /* the read with valid data (pass 1). */ t = READCSR(sc, SPROM_BASE + i); } else { /* and this one doesn't (pass 2). */ cfe_usleep(1000); /* Delay needed; value is empirical. */ t = READCSR(sc, SPROM_BASE + i); } dest[i+0] = (t >> 8) & 0xFF; dest[i+1] = (t >> 0) & 0xFF; t >>= 16; dest[i+2] = (t >> 8) & 0xFF; dest[i+3] = (t >> 0) & 0xFF; /* The following is a kludge, but otherwise setjmp is a one-shot. */ exc_handler.catch_exc = 1; } exc_cleanup_block(jb); return 0;}static voidsprom_dump(uint8_t srom[]){ int i; xprintf("BCM4401: SPROM data:"); for (i = 0; i < SPROM_SIZE; i++) { if (i % 16 == 0) xprintf("\n %02x: ", i); xprintf(" %02x", srom[i]); } xprintf("\n");}static intbcm4401_set_hw_addr(bcm4401_softc *sc, uint8_t addr[]){ uint32_t enet_upper, enet_lower; int timeout; enet_upper = (addr[0] << 8) | addr[1]; enet_lower = (addr[2] << 24) | (addr[3] << 16) | (addr[4] << 8) | addr[5]; WRITECSR(sc, R_CAM_DATA_H, M_CAM_VB | V_CAM_CD_H(enet_upper)); WRITECSR(sc, R_CAM_DATA_L, V_CAM_CD_L(enet_lower)); WRITECSR(sc, R_CAM_CONTROL, V_CAMCTL_IX(0) | M_CAMCTL_CW); for (timeout = CFE_HZ; timeout > 0; timeout -= CFE_HZ/10) { if ((READCSR(sc, R_CAM_CONTROL) & M_CAMCTL_CB) != 0) break; cfe_sleep(1); } if (timeout <= 0) return -1; return 0;}static voidbcm4401_set_linkspeed(bcm4401_softc *sc){ uint32_t ctrl; ctrl = READCSR(sc, R_XMT_CONTROL1); switch (sc->linkspeed) { case ETHER_SPEED_100FDX: case ETHER_SPEED_10FDX: ctrl |= (M_TCTL_FD | M_TCTL_SB); break; default: ctrl &= ~(M_TCTL_FD | M_TCTL_SB); break; } WRITECSR(sc, R_XMT_CONTROL1, ctrl);}static voidbcm4401_hwinit(bcm4401_softc *sc){ if (sc->state == eth_state_uninit) { uint32_t ctrl; bcm4401_reset(sc); mii_probe(sc); mii_dump(sc, "initial PHY"); sc->phy_interrupt = 0; if (sc->phy_vendor == OUI_BCMxx) { uint16_t phy_isr; switch (sc->phy_device) { case DEV_BCM5201: case DEV_BCM5221: case DEV_BCM4401: phy_isr = mii_read(sc, R_INTERRUPT); phy_isr &= ~(M_PHYINT_LI | M_PHYINT_SI | M_PHYINT_FD | M_PHYINT_MI); phy_isr |= M_PHYINT_IE; mii_write(sc, R_INTERRUPT, phy_isr); sc->phy_interrupt = 1; break; default: break; } } mii_autonegotiate(sc); if (sc->phy_interrupt) { (void)mii_read(sc, R_INTERRUPT); /* clear any pending */ } bcm4401_set_hw_addr(sc, sc->hwaddr); WRITECSR(sc, R_CAM_CONTROL, M_CAMCTL_CE); /* XXX Set the transmit watermark here, if needed. */ /* Initialize the receive channel. */ WRITECSR(sc, R_RCV_PTR, 0); WRITECSR(sc, R_RCV_CONTROL, M_RCTL_RE | V_RCTL_RO(PKTBUF_RX_OFFSET)); WRITECSR(sc, R_RCV_ADDR, PTR_TO_PCI(sc->rxdscrmem)); WRITECSR(sc, R_RCV_PTR, PTR_TO_PCI(sc->rxdscr_add) & 0xFFF); /* Initialize the transmit channel. */ WRITECSR(sc, R_XMT_PTR, 0); WRITECSR(sc, R_XMT_CONTROL, M_XCTL_XE); WRITECSR(sc, R_XMT_ADDR, PTR_TO_PCI(sc->txdscrmem)); /* Modify Ethernet RX MAC settings (probably obsolete). */ WRITECSR(sc, R_EMAC_XMT_MAX_BURST, 32); WRITECSR(sc, R_EMAC_RCV_MAX_BURST, 32);#if 0 WRITECSR(sc, R_RCV_CONFIG, M_RCFG_AM); /* All multicast */#else WRITECSR(sc, R_RCV_CONFIG, 0);#endif WRITECSR(sc, R_RCV_MAX_LENGTH, MAX_ETHER_PACK); ctrl = READCSR(sc, R_EMAC_CONTROL); ctrl |= M_EMCTL_CC; WRITECSR(sc, R_EMAC_CONTROL, ctrl); bcm4401_set_linkspeed(sc); WRITECSR(sc, R_XMT_MAX_LENGTH, MAX_ETHER_PACK); WRITECSR(sc, R_INT_RECV_LAZY, V_INTLZY_FC(1) | V_INTLZY_TO(100)); /* Enable the MAC */ ctrl = READCSR(sc, R_ENET_CONTROL); ctrl |= M_ECTL_EE; WRITECSR(sc, R_ENET_CONTROL, ctrl); sc->state = eth_state_on; }}static voidbcm4401_setspeed(bcm4401_softc *sc, int speed){ /* XXX Not yet implemented - autonegotiation only. */ (void)mii_set_speed;}static voidbcm4401_setloopback(bcm4401_softc *sc, int mode){ /* XXX Not yet implemented. */}static voidbcm4401_isr(void *arg){ bcm4401_softc *sc = (bcm4401_softc *)arg; uint32_t status;#if IPOLL sc->interrupts++;#endif for (;;) { /* Read and clear the interrupt status. */ status = READCSR(sc, R_INT_STATUS); status &= sc->intmask; if (status == 0) break; WRITECSR(sc, R_INT_STATUS, status); /* write-to-clear */ /* XXX Handle SERR, etc. */ if (status & M_INT_RI) {#if IPOLL sc->rx_interrupts++;#endif bcm4401_procrxring(sc); } if (status & M_INT_XI) {#if IPOLL sc->tx_interrupts++;#endif bcm4401_proctxring(sc); } if (status & M_INT_TO) { sc->slow_poll = 1; } if (status & (M_INT_XU | M_INT_RO)) { if (status & M_INT_XU) { xprintf("BCM4401: tx underrun, %08x\n", status); /* XXX Try to restart */ } if (status & M_INT_RO) { xprintf("BCM4401: rx overrun, %08x\n", status); /* XXX Try to restart */ } } }}static voidbcm4401_start(bcm4401_softc *sc){ bcm4401_hwinit(sc); /* Set up loopback here */ WRITECSR(sc, R_GP_TIMER, 0); /* stop the timer */ sc->intmask = 0; WRITECSR(sc, R_INT_MASK, 0); (void)READCSR(sc, R_INT_STATUS); /* clear any pending */ sc->intmask = (M_INT_RI | M_INT_XI); /* XXX add errors */ if (sc->phy_interrupt) sc->intmask |= M_INT_TO;#if IPOLL cfe_request_irq(sc->irq, bcm4401_isr, sc, CFE_IRQ_FLAGS_SHARED, 0); WRITECSR(sc, R_INT_MASK, sc->intmask);#endif sc->slow_poll = 0; if (sc->phy_interrupt) { WRITECSR(sc, R_GP_TIMER, GP_TIMER_HZ/4); } sc->state = eth_state_on;}static voidbcm4401_stop(bcm4401_softc *sc){ uint32_t ctl, status; int i; /* Cancel the timer */ if (sc->phy_interrupt) { WRITECSR(sc, R_GP_TIMER, 0); (void)READCSR(sc, R_GP_TIMER); } sc->slow_poll = 0; /* Make sure that no further interrutps will be processed. */ sc->intmask = 0; WRITECSR(sc, R_INT_MASK, 0); (void)READCSR(sc, R_INT_MASK); /* push */ (void)READCSR(sc, R_INT_STATUS); /* Shut down MAC */ WRITECSR(sc, R_ENET_CONTROL, M_ECTL_ED); for (i = 1000; i > 0; i--) { ctl = READCSR(sc, R_ENET_CONTROL); if ((ctl & M_ECTL_ED) == 0) break; cfe_usleep(100); } if (i == 0) xprintf("%s: cannot clear MAC\n", bcm4401_devname(sc)); /* Shut down DMA engines */ WRITECSR(sc, R_XMT_CONTROL, 0); for (i = 1000; i > 0; i--) { status = READCSR(sc, R_XMT_STATUS); if (G_XSTAT_XS(status) == K_XS_DISABLED) break; cfe_usleep(100); } if (i == 0) xprintf("%s: cannot clear tx DMA\n", bcm4401_devname(sc)); WRITECSR(sc, R_RCV_CONTROL, 0); for (i = 1000; i > 0; i--) { status = READCSR(sc, R_RCV_STATUS); if (G_RSTAT_RS(status) == K_RS_DISABLED) break; cfe_usleep(100); } if (i == 0) xprintf("%s: cannot clear rx DMA\n", bcm4401_devname(sc)); status = READCSR(sc, R_INT_STATUS); WRITECSR(sc, R_INT_STATUS, status);#if IPOLL cfe_free_irq(sc->irq, 0);#endif /* Leave the mii inteface enabled */ mii_enable(sc);}/* Declarations for CFE Device Driver Interface routines */static int bcm4401_ether_open(cfe_devctx_t *ctx);static int bcm4401_ether_read(cfe_devctx_t *ctx,iocb_buffer_t *buffer);static int bcm4401_ether_inpstat(cfe_devctx_t *ctx,iocb_inpstat_t *inpstat);static int bcm4401_ether_write(cfe_devctx_t *ctx,iocb_buffer_t *buffer);static int bcm4401_ether_ioctl(cfe_devctx_t *ctx,iocb_buffer_t *buffer);static int bcm4401_ether_close(cfe_devctx_t *ctx);static void bcm4401_ether_poll(cfe_devctx_t *ctx, int64_t ticks);static void bcm4401_ether_reset(void *softc);/* CFE Device Driver dispatch structure */const static cfe_devdisp_t bcm4401_ether_dispatch = { bcm4401_ether_open, bcm4401_ether_read, bcm4401_ether_inpstat, bcm4401_ether_write, bcm4401_ether_ioctl, bcm4401_ether_close, bcm4401_ether_poll, bcm4401_ether_reset};#if CFG_PCI/* CFE Device Driver probe functions for PCI NIC (bcm4401) */const cfe_driver_t bcm4401drv = { "BCM4401 Ethernet", "eth", CFE_DEV_NETWORK, &bcm4401_ether_dispatch, bcm4401_ether_probe};static voidbcm4401_pciconfig(bcm4401_softc *sc){ uint32_t oldsb; uint32_t xlat; oldsb = pci_conf_read(sc->tag, PCI_PCIBAR0WINDOW_REG); pci_conf_write(sc->tag, PCI_PCIBAR0WINDOW_REG, SB_PCI_BASE); (void)pci_conf_read(sc->tag, PCI_PCIBAR0WINDOW_REG); /* push */ WRITECSR(sc, R_SBINTVEC, V_SBINT_MK(K_SBINT_ENET_MAC)); xlat = READCSR(sc, R_SB_TO_PCI_TRANSLATION2); xlat |= (M_SBXLAT_PE | M_SBXLAT_WB); WRITECSR(sc, R_SB_TO_PCI_TRANSLATION2, xlat); (void)READCSR(sc, R_SB_TO_PCI_TRANSLATION2); /* push */ pci_conf_write(sc->tag, PCI_PCIBAR0WINDOW_REG, oldsb); (void)pci_conf_read(sc->tag, PCI_PCIBAR0WINDOW_REG); /* push */}static intbcm4401_ether_attach(cfe_driver_t *drv, pcitag_t tag){ bcm4401_softc *sc; uint32_t device; uint32_t class; phys_addr_t pa;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -