📄 fcc_enet.c
字号:
volatile immap_t *immap; volatile iop8260_t *io; immap = (immap_t *)IMAP_ADDR; /* and to internal registers */ io = &immap->im_ioport; np = sizeof(fcc_ports) / sizeof(fcc_info_t); fip = fcc_ports; while (np-- > 0) { /* Allocate some private information. */ cep = (struct fcc_enet_private *) kmalloc(sizeof(*cep), GFP_KERNEL); if (cep == NULL) return -ENOMEM; __clear_user(cep,sizeof(*cep)); spin_lock_init(&cep->lock); cep->fip = fip; /* Create an Ethernet device instance. */ dev = init_etherdev(0, 0); dev->priv = cep; init_fcc_shutdown(fip, cep, immap); init_fcc_ioports(fip, io, immap); init_fcc_param(fip, dev, immap); dev->base_addr = (unsigned long)(cep->ep); /* The CPM Ethernet specific entries in the device * structure. */ dev->open = fcc_enet_open; dev->hard_start_xmit = fcc_enet_start_xmit; dev->tx_timeout = fcc_enet_timeout; dev->watchdog_timeo = TX_TIMEOUT; dev->stop = fcc_enet_close; dev->get_stats = fcc_enet_get_stats; dev->set_multicast_list = set_multicast_list; init_fcc_startup(fip, dev); printk("%s: FCC ENET Version 0.2, ", dev->name); for (i=0; i<5; i++) printk("%02x:", dev->dev_addr[i]); printk("%02x\n", dev->dev_addr[5]); /* This is just a hack for now that works only on the EST * board, or anything else that has MDIO/CK configured. * It is mainly to test the MII software clocking. */ mii_discover_phy_poll(fip); fip++; } return 0;}/* Make sure the device is shut down during initialization.*/static void __initinit_fcc_shutdown(fcc_info_t *fip, struct fcc_enet_private *cep, volatile immap_t *immap){ volatile fcc_enet_t *ep; volatile fcc_t *fccp; /* Get pointer to FCC area in parameter RAM. */ ep = (fcc_enet_t *)(&immap->im_dprambase[fip->fc_proff]); /* And another to the FCC register area. */ fccp = (volatile fcc_t *)(&immap->im_fcc[fip->fc_fccnum]); cep->fccp = fccp; /* Keep the pointers handy */ cep->ep = ep; /* Disable receive and transmit in case someone left it running. */ fccp->fcc_gfmr &= ~(FCC_GFMR_ENR | FCC_GFMR_ENT);}/* Initialize the I/O pins for the FCC Ethernet.*/static void __initinit_fcc_ioports(fcc_info_t *fip, volatile iop8260_t *io, volatile immap_t *immap){ /* FCC1 pins are on port A/C. FCC2/3 are port B/C. */ if (fip->fc_proff == PROFF_FCC1) { /* Configure port A and C pins for FCC1 Ethernet. */ io->iop_pdira &= ~PA1_DIRA0; io->iop_pdira |= PA1_DIRA1; io->iop_psora &= ~PA1_PSORA0; io->iop_psora |= PA1_PSORA1; io->iop_ppara |= (PA1_DIRA0 | PA1_DIRA1); } if (fip->fc_proff == PROFF_FCC2) { /* Configure port B and C pins for FCC Ethernet. */ io->iop_pdirb &= ~PB2_DIRB0; io->iop_pdirb |= PB2_DIRB1; io->iop_psorb &= ~PB2_PSORB0; io->iop_psorb |= PB2_PSORB1; io->iop_pparb |= (PB2_DIRB0 | PB2_DIRB1); } if (fip->fc_proff == PROFF_FCC3) { /* Configure port B and C pins for FCC Ethernet. */ io->iop_pdirb &= ~PB3_DIRB0; io->iop_pdirb |= PB3_DIRB1; io->iop_psorb &= ~PB3_PSORB0; io->iop_psorb |= PB3_PSORB1; io->iop_pparb |= (PB3_DIRB0 | PB3_DIRB1); } /* Port C has clocks...... */ io->iop_psorc &= ~(fip->fc_trxclocks); io->iop_pdirc &= ~(fip->fc_trxclocks); io->iop_pparc |= fip->fc_trxclocks; /* ....and the MII serial clock/data. */ io->iop_pdatc |= (fip->fc_mdio | fip->fc_mdck); io->iop_podrc |= fip->fc_mdio; io->iop_pdirc |= (fip->fc_mdio | fip->fc_mdck); io->iop_pparc &= ~(fip->fc_mdio | fip->fc_mdck); /* Configure Serial Interface clock routing. * First, clear all FCC bits to zero, * then set the ones we want. */ immap->im_cpmux.cmx_fcr &= ~(fip->fc_clockmask); immap->im_cpmux.cmx_fcr |= fip->fc_clockroute;}static void __initinit_fcc_param(fcc_info_t *fip, struct net_device *dev, volatile immap_t *immap){ unsigned char *eap; unsigned long mem_addr; bd_t *bd; int i, j; struct fcc_enet_private *cep; volatile fcc_enet_t *ep; volatile cbd_t *bdp; volatile cpm8260_t *cp; cep = (struct fcc_enet_private *)(dev->priv); ep = cep->ep; cp = cpmp; bd = (bd_t *)__res; /* Zero the whole thing.....I must have missed some individually. * It works when I do this. */ memset((char *)ep, 0, sizeof(fcc_enet_t)); /* Allocate space for the buffer descriptors in the DP ram. * These are relative offsets in the DP ram address space. * Initialize base addresses for the buffer descriptors. */#if 0 /* I really want to do this, but for some reason it doesn't * work with the data cache enabled, so I allocate from the * main memory instead. */ i = m8260_cpm_dpalloc(sizeof(cbd_t) * RX_RING_SIZE, 8); ep->fen_genfcc.fcc_rbase = (uint)&immap->im_dprambase[i]; cep->rx_bd_base = (cbd_t *)&immap->im_dprambase[i]; i = m8260_cpm_dpalloc(sizeof(cbd_t) * TX_RING_SIZE, 8); ep->fen_genfcc.fcc_tbase = (uint)&immap->im_dprambase[i]; cep->tx_bd_base = (cbd_t *)&immap->im_dprambase[i];#else cep->rx_bd_base = (cbd_t *)m8260_cpm_hostalloc(sizeof(cbd_t) * RX_RING_SIZE, 8); ep->fen_genfcc.fcc_rbase = __pa(cep->rx_bd_base); cep->tx_bd_base = (cbd_t *)m8260_cpm_hostalloc(sizeof(cbd_t) * TX_RING_SIZE, 8); ep->fen_genfcc.fcc_tbase = __pa(cep->tx_bd_base);#endif cep->dirty_tx = cep->cur_tx = cep->tx_bd_base; cep->cur_rx = cep->rx_bd_base; ep->fen_genfcc.fcc_rstate = (CPMFCR_GBL | CPMFCR_EB) << 24; ep->fen_genfcc.fcc_tstate = (CPMFCR_GBL | CPMFCR_EB) << 24; /* Set maximum bytes per receive buffer. * It must be a multiple of 32. */ ep->fen_genfcc.fcc_mrblr = PKT_MAXBLR_SIZE; /* Allocate space in the reserved FCC area of DPRAM for the * internal buffers. No one uses this space (yet), so we * can do this. Later, we will add resource management for * this area. */ mem_addr = CPM_FCC_SPECIAL_BASE + (fip->fc_fccnum * 128); ep->fen_genfcc.fcc_riptr = mem_addr; ep->fen_genfcc.fcc_tiptr = mem_addr+32; ep->fen_padptr = mem_addr+64; memset((char *)(&(immap->im_dprambase[(mem_addr+64)])), 0x88, 32); ep->fen_genfcc.fcc_rbptr = 0; ep->fen_genfcc.fcc_tbptr = 0; ep->fen_genfcc.fcc_rcrc = 0; ep->fen_genfcc.fcc_tcrc = 0; ep->fen_genfcc.fcc_res1 = 0; ep->fen_genfcc.fcc_res2 = 0; ep->fen_camptr = 0; /* CAM isn't used in this driver */ /* Set CRC preset and mask. */ ep->fen_cmask = 0xdebb20e3; ep->fen_cpres = 0xffffffff; ep->fen_crcec = 0; /* CRC Error counter */ ep->fen_alec = 0; /* alignment error counter */ ep->fen_disfc = 0; /* discard frame counter */ ep->fen_retlim = 15; /* Retry limit threshold */ ep->fen_pper = 0; /* Normal persistence */ /* Clear hash filter tables. */ ep->fen_gaddrh = 0; ep->fen_gaddrl = 0; ep->fen_iaddrh = 0; ep->fen_iaddrl = 0; /* Clear the Out-of-sequence TxBD. */ ep->fen_tfcstat = 0; ep->fen_tfclen = 0; ep->fen_tfcptr = 0; ep->fen_mflr = PKT_MAXBUF_SIZE; /* maximum frame length register */ ep->fen_minflr = PKT_MINBUF_SIZE; /* minimum frame length register */ /* Set Ethernet station address. * * This is supplied in the board information structure, so we * copy that into the controller. * So, far we have only been given one Ethernet address. We make * it unique by setting a few bits in the upper byte of the * non-static part of the address. */ eap = (unsigned char *)&(ep->fen_paddrh); for (i=5; i>=0; i--) { if (i == 3) { dev->dev_addr[i] = bd->bi_enetaddr[i]; dev->dev_addr[i] |= (1 << (7 - fip->fc_fccnum)); *eap++ = dev->dev_addr[i]; } else { *eap++ = dev->dev_addr[i] = bd->bi_enetaddr[i]; } } ep->fen_taddrh = 0; ep->fen_taddrm = 0; ep->fen_taddrl = 0; ep->fen_maxd1 = PKT_MAXDMA_SIZE; /* maximum DMA1 length */ ep->fen_maxd2 = PKT_MAXDMA_SIZE; /* maximum DMA2 length */ /* Clear stat counters, in case we ever enable RMON. */ ep->fen_octc = 0; ep->fen_colc = 0; ep->fen_broc = 0; ep->fen_mulc = 0; ep->fen_uspc = 0; ep->fen_frgc = 0; ep->fen_ospc = 0; ep->fen_jbrc = 0; ep->fen_p64c = 0; ep->fen_p65c = 0; ep->fen_p128c = 0; ep->fen_p256c = 0; ep->fen_p512c = 0; ep->fen_p1024c = 0; ep->fen_rfthr = 0; /* Suggested by manual */ ep->fen_rfcnt = 0; ep->fen_cftype = 0; /* Now allocate the host memory pages and initialize the * buffer descriptors. */ bdp = cep->tx_bd_base; for (i=0; i<TX_RING_SIZE; i++) { /* Initialize the BD for every fragment in the page. */ bdp->cbd_sc = 0; bdp->cbd_datlen = 0; bdp->cbd_bufaddr = 0; bdp++; } /* Set the last buffer to wrap. */ bdp--; bdp->cbd_sc |= BD_SC_WRAP; bdp = cep->rx_bd_base; for (i=0; i<FCC_ENET_RX_PAGES; i++) { /* Allocate a page. */ mem_addr = __get_free_page(GFP_KERNEL); /* Initialize the BD for every fragment in the page. */ for (j=0; j<FCC_ENET_RX_FRPPG; j++) { bdp->cbd_sc = BD_ENET_RX_EMPTY | BD_ENET_RX_INTR; bdp->cbd_datlen = 0; bdp->cbd_bufaddr = __pa(mem_addr); mem_addr += FCC_ENET_RX_FRSIZE; bdp++; } } /* Set the last buffer to wrap. */ bdp--; bdp->cbd_sc |= BD_SC_WRAP; /* Let's re-initialize the channel now. We have to do it later * than the manual describes because we have just now finished * the BD initialization. */ cp->cp_cpcr = mk_cr_cmd(fip->fc_cpmpage, fip->fc_cpmblock, 0x0c, CPM_CR_INIT_TRX) | CPM_CR_FLG; while (cp->cp_cpcr & CPM_CR_FLG); cep->skb_cur = cep->skb_dirty = 0;}/* Let 'er rip.*/static void __initinit_fcc_startup(fcc_info_t *fip, struct net_device *dev){ volatile fcc_t *fccp; struct fcc_enet_private *cep; cep = (struct fcc_enet_private *)(dev->priv); fccp = cep->fccp; fccp->fcc_fcce = 0xffff; /* Clear any pending events */ /* Enable interrupts for transmit error, complete frame * received, and any transmit buffer we have also set the * interrupt flag. */ fccp->fcc_fccm = (FCC_ENET_TXE | FCC_ENET_RXF | FCC_ENET_TXB); /* Install our interrupt handler. */ if (request_8xxirq(fip->fc_interrupt, fcc_enet_interrupt, 0, "fenet", dev) < 0) printk("Can't get FCC IRQ %d\n", fip->fc_interrupt); /* Set GFMR to enable Ethernet operating mode. */ fccp->fcc_gfmr = (FCC_GFMR_TCI | FCC_GFMR_MODE_ENET); /* Set sync/delimiters. */ fccp->fcc_fdsr = 0xd555; /* Set protocol specific processing mode for Ethernet. * This has to be adjusted for Full Duplex operation after we can * determine how to detect that. */ fccp->fcc_fpsmr = FCC_PSMR_ENCRC; /* And last, enable the transmit and receive processing. */ fccp->fcc_gfmr |= (FCC_GFMR_ENR | FCC_GFMR_ENT);}/* MII command/status interface. * I'm not going to describe all of the details. You can find the * protocol definition in many other places, including the data sheet * of most PHY parts. * I wonder what "they" were thinking (maybe weren't) when they leave * the I2C in the CPM but I have to toggle these bits...... */static uintmii_send_receive(fcc_info_t *fip, uint cmd){ unsigned long flags; uint retval; int read_op, i; volatile immap_t *immap; volatile iop8260_t *io; immap = (immap_t *)IMAP_ADDR; io = &immap->im_ioport; /* When we get here, both clock and data are high, outputs. * Output is open drain. * Data transitions on high->low clock, is valid on low->high clock. * Spec says edge transitions no closer than 160 nSec, minimum clock * cycle 400 nSec. I could only manage about 500 nSec edges with * an XOR loop, so I won't worry about delays yet. * I disable interrupts during bit flipping to ensure atomic * updates of the registers. I do lots of interrupt disable/enable * to ensure we don't hang out too long with interrupts disabled. */ /* First, crank out 32 1-bits as preamble. * This is 64 transitions to clock the bits, with clock/data * left high. */ save_flags(flags); cli(); for (i=0; i<64; i++) { io->iop_pdatc ^= fip->fc_mdck; udelay(0); } restore_flags(flags); read_op = ((cmd & 0xf0000000) == 0x60000000); /* We return the command word on a write op, or the command portion * plus the new data on a read op. This is what the 8xx FEC does, * and it allows the functions to simply look at the returned value * and know the PHY/register as well. */ if (read_op) retval = cmd; else retval = (cmd >> 16); /* Clock out the first 16 MS bits of the command. */ save_flags(flags); cli(); for (i=0; i<16; i++) { io->iop_pdatc &= ~(fip->fc_mdck); if (cmd & 0x80000000) io->iop_pdatc |= fip->fc_mdio; else io->iop_pdatc &= ~(fip->fc_mdio); cmd <<= 1; io->iop_pdatc |= fip->fc_mdck; udelay(0); } /* Do the turn-around. If read op, we make the IO and input. * If write op, do the 1/0 thing. */ io->iop_pdatc &= ~(fip->fc_mdck); if (read_op) io->iop_pdirc &= ~(fip->fc_mdio); else io->iop_pdatc |= fip->fc_mdio; io->iop_pdatc |= fip->fc_mdck; /* I do this mainly to get just a little delay. */ restore_flags(flags); save_flags(flags); cli(); io->iop_pdatc &= ~(fip->fc_mdck); io->iop_pdirc &= ~(fip->fc_mdio); io->iop_pdatc |= fip->fc_mdck; restore_flags(flags); save_flags(flags); cli(); /* For read, clock in 16 bits. For write, clock out * rest of command. */ if (read_op) { io->iop_pdatc &= ~(fip->fc_mdck); udelay(0); for (i=0; i<16; i++) { io->iop_pdatc |= fip->fc_mdck; udelay(0); retval <<= 1; if (io->iop_pdatc & fip->fc_mdio) retval |= 1; io->iop_pdatc &= ~(fip->fc_mdck); udelay(0); } } else { for (i=0; i<16; i++) { io->iop_pdatc &= ~(fip->fc_mdck); if (cmd & 0x80000000) io->iop_pdatc |= fip->fc_mdio; else io->iop_pdatc &= ~(fip->fc_mdio); cmd <<= 1; io->iop_pdatc |= fip->fc_mdck; udelay(0); } io->iop_pdatc &= ~(fip->fc_mdck); } restore_flags(flags); /* Some diagrams show two 1 bits for "idle". I don't know if * this is really necessary or if it was just to indicate nothing * is going to happen for a while. * Make the data pin an output, set the data high, and clock it. */ save_flags(flags); cli(); io->iop_pdatc |= fip->fc_mdio; io->iop_pdirc |= fip->fc_mdio; for (i=0; i<3; i++) io->iop_pdatc ^= fip->fc_mdck; restore_flags(flags); /* We exit with the same conditions as entry. */ return(retval);}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -