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

📄 fcc_enet.c

📁 《嵌入式系统设计与实例开发实验教材二源码》Linux内核移植与编译实验
💻 C
📖 第 1 页 / 共 3 页
字号:
	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 + -