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

📄 tsec.c

📁 U-boot源码 ARM7启动代码
💻 C
📖 第 1 页 / 共 3 页
字号:
/* * Freescale Three Speed Ethernet Controller driver * * This software may be used and distributed according to the * terms of the GNU Public License, Version 2, incorporated * herein by reference. * * Copyright 2004, 2007 Freescale Semiconductor, Inc. * (C) Copyright 2003, Motorola, Inc. * author Andy Fleming * */#include <config.h>#include <common.h>#include <malloc.h>#include <net.h>#include <command.h>#if defined(CONFIG_TSEC_ENET)#include "tsec.h"#include "miiphy.h"DECLARE_GLOBAL_DATA_PTR;#define TX_BUF_CNT		2static uint rxIdx;		/* index of the current RX buffer */static uint txIdx;		/* index of the current TX buffer */typedef volatile struct rtxbd {	txbd8_t txbd[TX_BUF_CNT];	rxbd8_t rxbd[PKTBUFSRX];} RTXBD;struct tsec_info_struct {	unsigned int phyaddr;	u32 flags;	unsigned int phyregidx;};/* The tsec_info structure contains 3 values which the * driver uses to determine how to operate a given ethernet * device. The information needed is: *  phyaddr - The address of the PHY which is attached to *	the given device. * *  flags - This variable indicates whether the device *	supports gigabit speed ethernet, and whether it should be *	in reduced mode. * *  phyregidx - This variable specifies which ethernet device *	controls the MII Management registers which are connected *	to the PHY.  For now, only TSEC1 (index 0) has *	access to the PHYs, so all of the entries have "0". * * The values specified in the table are taken from the board's * config file in include/configs/.  When implementing a new * board with ethernet capability, it is necessary to define: *   TSECn_PHY_ADDR *   TSECn_PHYIDX * * for n = 1,2,3, etc.  And for FEC: *   FEC_PHY_ADDR *   FEC_PHYIDX */static struct tsec_info_struct tsec_info[] = {#ifdef CONFIG_TSEC1	{TSEC1_PHY_ADDR, TSEC1_FLAGS, TSEC1_PHYIDX},#else	{0, 0, 0},#endif#ifdef CONFIG_TSEC2	{TSEC2_PHY_ADDR, TSEC2_FLAGS, TSEC2_PHYIDX},#else	{0, 0, 0},#endif#ifdef CONFIG_MPC85XX_FEC	{FEC_PHY_ADDR, FEC_FLAGS, FEC_PHYIDX},#else#ifdef CONFIG_TSEC3	{TSEC3_PHY_ADDR, TSEC3_FLAGS, TSEC3_PHYIDX},#else	{0, 0, 0},#endif#ifdef CONFIG_TSEC4	{TSEC4_PHY_ADDR, TSEC4_FLAGS, TSEC4_PHYIDX},#else	{0, 0, 0},#endif	/* CONFIG_TSEC4 */#endif	/* CONFIG_MPC85XX_FEC */};#define MAXCONTROLLERS	(4)static int relocated = 0;static struct tsec_private *privlist[MAXCONTROLLERS];#ifdef __GNUC__static RTXBD rtx __attribute__ ((aligned(8)));#else#error "rtx must be 64-bit aligned"#endifstatic int tsec_send(struct eth_device *dev,		     volatile void *packet, int length);static int tsec_recv(struct eth_device *dev);static int tsec_init(struct eth_device *dev, bd_t * bd);static void tsec_halt(struct eth_device *dev);static void init_registers(volatile tsec_t * regs);static void startup_tsec(struct eth_device *dev);static int init_phy(struct eth_device *dev);void write_phy_reg(struct tsec_private *priv, uint regnum, uint value);uint read_phy_reg(struct tsec_private *priv, uint regnum);struct phy_info *get_phy_info(struct eth_device *dev);void phy_run_commands(struct tsec_private *priv, struct phy_cmd *cmd);static void adjust_link(struct eth_device *dev);static void relocate_cmds(void);#if defined(CONFIG_MII) || defined(CONFIG_CMD_MII) \	&& !defined(BITBANGMII)static int tsec_miiphy_write(char *devname, unsigned char addr,			     unsigned char reg, unsigned short value);static int tsec_miiphy_read(char *devname, unsigned char addr,			    unsigned char reg, unsigned short *value);#endif#ifdef CONFIG_MCAST_TFTPstatic int tsec_mcast_addr (struct eth_device *dev, u8 mcast_mac, u8 set);#endif/* Initialize device structure. Returns success if PHY * initialization succeeded (i.e. if it recognizes the PHY) */int tsec_initialize(bd_t * bis, int index, char *devname){	struct eth_device *dev;	int i;	struct tsec_private *priv;	dev = (struct eth_device *)malloc(sizeof *dev);	if (NULL == dev)		return 0;	memset(dev, 0, sizeof *dev);	priv = (struct tsec_private *)malloc(sizeof(*priv));	if (NULL == priv)		return 0;	privlist[index] = priv;	priv->regs = (volatile tsec_t *)(TSEC_BASE_ADDR + index * TSEC_SIZE);	priv->phyregs = (volatile tsec_t *)(TSEC_BASE_ADDR +					    tsec_info[index].phyregidx *					    TSEC_SIZE);	priv->phyaddr = tsec_info[index].phyaddr;	priv->flags = tsec_info[index].flags;	sprintf(dev->name, devname);	dev->iobase = 0;	dev->priv = priv;	dev->init = tsec_init;	dev->halt = tsec_halt;	dev->send = tsec_send;	dev->recv = tsec_recv;#ifdef CONFIG_MCAST_TFTP	dev->mcast = tsec_mcast_addr;#endif	/* Tell u-boot to get the addr from the env */	for (i = 0; i < 6; i++)		dev->enetaddr[i] = 0;	eth_register(dev);	/* Reset the MAC */	priv->regs->maccfg1 |= MACCFG1_SOFT_RESET;	priv->regs->maccfg1 &= ~(MACCFG1_SOFT_RESET);#if defined(CONFIG_MII) || defined(CONFIG_CMD_MII) \	&& !defined(BITBANGMII)	miiphy_register(dev->name, tsec_miiphy_read, tsec_miiphy_write);#endif	/* Try to initialize PHY here, and return */	return init_phy(dev);}/* Initializes data structures and registers for the controller, * and brings the interface up.	 Returns the link status, meaning * that it returns success if the link is up, failure otherwise. * This allows u-boot to find the first active controller. */int tsec_init(struct eth_device *dev, bd_t * bd){	uint tempval;	char tmpbuf[MAC_ADDR_LEN];	int i;	struct tsec_private *priv = (struct tsec_private *)dev->priv;	volatile tsec_t *regs = priv->regs;	/* Make sure the controller is stopped */	tsec_halt(dev);	/* Init MACCFG2.  Defaults to GMII */	regs->maccfg2 = MACCFG2_INIT_SETTINGS;	/* Init ECNTRL */	regs->ecntrl = ECNTRL_INIT_SETTINGS;	/* Copy the station address into the address registers.	 * Backwards, because little endian MACS are dumb */	for (i = 0; i < MAC_ADDR_LEN; i++) {		tmpbuf[MAC_ADDR_LEN - 1 - i] = dev->enetaddr[i];	}	regs->macstnaddr1 = *((uint *) (tmpbuf));	tempval = *((uint *) (tmpbuf + 4));	regs->macstnaddr2 = tempval;	/* reset the indices to zero */	rxIdx = 0;	txIdx = 0;	/* Clear out (for the most part) the other registers */	init_registers(regs);	/* Ready the device for tx/rx */	startup_tsec(dev);	/* If there's no link, fail */	return priv->link;}/* Write value to the device's PHY through the registers * specified in priv, modifying the register specified in regnum. * It will wait for the write to be done (or for a timeout to * expire) before exiting */void write_phy_reg(struct tsec_private *priv, uint regnum, uint value){	volatile tsec_t *regbase = priv->phyregs;	uint phyid = priv->phyaddr;	int timeout = 1000000;	regbase->miimadd = (phyid << 8) | regnum;	regbase->miimcon = value;	asm("sync");	timeout = 1000000;	while ((regbase->miimind & MIIMIND_BUSY) && timeout--) ;}/* Reads register regnum on the device's PHY through the * registers specified in priv.	 It lowers and raises the read * command, and waits for the data to become valid (miimind * notvalid bit cleared), and the bus to cease activity (miimind * busy bit cleared), and then returns the value */uint read_phy_reg(struct tsec_private *priv, uint regnum){	uint value;	volatile tsec_t *regbase = priv->phyregs;	uint phyid = priv->phyaddr;	/* Put the address of the phy, and the register	 * number into MIIMADD */	regbase->miimadd = (phyid << 8) | regnum;	/* Clear the command register, and wait */	regbase->miimcom = 0;	asm("sync");	/* Initiate a read command, and wait */	regbase->miimcom = MIIM_READ_COMMAND;	asm("sync");	/* Wait for the the indication that the read is done */	while ((regbase->miimind & (MIIMIND_NOTVALID | MIIMIND_BUSY))) ;	/* Grab the value read from the PHY */	value = regbase->miimstat;	return value;}/* Discover which PHY is attached to the device, and configure it * properly.  If the PHY is not recognized, then return 0 * (failure).  Otherwise, return 1 */static int init_phy(struct eth_device *dev){	struct tsec_private *priv = (struct tsec_private *)dev->priv;	struct phy_info *curphy;	volatile tsec_t *regs = (volatile tsec_t *)(TSEC_BASE_ADDR);	/* Assign a Physical address to the TBI */	regs->tbipa = CFG_TBIPA_VALUE;	regs = (volatile tsec_t *)(TSEC_BASE_ADDR + TSEC_SIZE);	regs->tbipa = CFG_TBIPA_VALUE;	asm("sync");	/* Reset MII (due to new addresses) */	priv->phyregs->miimcfg = MIIMCFG_RESET;	asm("sync");	priv->phyregs->miimcfg = MIIMCFG_INIT_VALUE;	asm("sync");	while (priv->phyregs->miimind & MIIMIND_BUSY) ;	if (0 == relocated)		relocate_cmds();	/* Get the cmd structure corresponding to the attached	 * PHY */	curphy = get_phy_info(dev);	if (curphy == NULL) {		priv->phyinfo = NULL;		printf("%s: No PHY found\n", dev->name);		return 0;	}	priv->phyinfo = curphy;	phy_run_commands(priv, priv->phyinfo->config);	return 1;}/* * Returns which value to write to the control register. * For 10/100, the value is slightly different */uint mii_cr_init(uint mii_reg, struct tsec_private * priv){	if (priv->flags & TSEC_GIGABIT)		return MIIM_CONTROL_INIT;	else		return MIIM_CR_INIT;}/* Parse the status register for link, and then do * auto-negotiation */uint mii_parse_sr(uint mii_reg, struct tsec_private * priv){	/*	 * Wait if the link is up, and autonegotiation is in progress	 * (ie - we're capable and it's not done)	 */	mii_reg = read_phy_reg(priv, MIIM_STATUS);	if ((mii_reg & MIIM_STATUS_LINK) && (mii_reg & PHY_BMSR_AUTN_ABLE)	    && !(mii_reg & PHY_BMSR_AUTN_COMP)) {		int i = 0;		puts("Waiting for PHY auto negotiation to complete");		while (!(mii_reg & PHY_BMSR_AUTN_COMP)) {			/*			 * Timeout reached ?			 */			if (i > PHY_AUTONEGOTIATE_TIMEOUT) {				puts(" TIMEOUT !\n");				priv->link = 0;				return 0;			}			if ((i++ % 1000) == 0) {				putc('.');			}			udelay(1000);	/* 1 ms */			mii_reg = read_phy_reg(priv, MIIM_STATUS);		}		puts(" done\n");		priv->link = 1;		udelay(500000);	/* another 500 ms (results in faster booting) */	} else {		if (mii_reg & MIIM_STATUS_LINK)			priv->link = 1;		else			priv->link = 0;	}	return 0;}/* Generic function which updates the speed and duplex.  If * autonegotiation is enabled, it uses the AND of the link * partner's advertised capabilities and our advertised * capabilities.  If autonegotiation is disabled, we use the * appropriate bits in the control register. * * Stolen from Linux's mii.c and phy_device.c */uint mii_parse_link(uint mii_reg, struct tsec_private *priv){	/* We're using autonegotiation */	if (mii_reg & PHY_BMSR_AUTN_ABLE) {		uint lpa = 0;		uint gblpa = 0;		/* Check for gigabit capability */		if (mii_reg & PHY_BMSR_EXT) {			/* We want a list of states supported by			 * both PHYs in the link			 */			gblpa = read_phy_reg(priv, PHY_1000BTSR);			gblpa &= read_phy_reg(priv, PHY_1000BTCR) << 2;		}		/* Set the baseline so we only have to set them		 * if they're different		 */		priv->speed = 10;		priv->duplexity = 0;		/* Check the gigabit fields */		if (gblpa & (PHY_1000BTSR_1000FD | PHY_1000BTSR_1000HD)) {			priv->speed = 1000;			if (gblpa & PHY_1000BTSR_1000FD)				priv->duplexity = 1;			/* We're done! */			return 0;		}		lpa = read_phy_reg(priv, PHY_ANAR);		lpa &= read_phy_reg(priv, PHY_ANLPAR);		if (lpa & (PHY_ANLPAR_TXFD | PHY_ANLPAR_TX)) {			priv->speed = 100;			if (lpa & PHY_ANLPAR_TXFD)				priv->duplexity = 1;		} else if (lpa & PHY_ANLPAR_10FD)			priv->duplexity = 1;	} else {		uint bmcr = read_phy_reg(priv, PHY_BMCR);		priv->speed = 10;		priv->duplexity = 0;		if (bmcr & PHY_BMCR_DPLX)			priv->duplexity = 1;		if (bmcr & PHY_BMCR_1000_MBPS)			priv->speed = 1000;		else if (bmcr & PHY_BMCR_100_MBPS)			priv->speed = 100;	}	return 0;}/* * Parse the BCM54xx status register for speed and duplex information. * The linux sungem_phy has this information, but in a table format. */uint mii_parse_BCM54xx_sr(uint mii_reg, struct tsec_private *priv){	switch((mii_reg & MIIM_BCM54xx_AUXSTATUS_LINKMODE_MASK) >> MIIM_BCM54xx_AUXSTATUS_LINKMODE_SHIFT){		case 1:			printf("Enet starting in 10BT/HD\n");			priv->duplexity = 0;			priv->speed = 10;			break;		case 2:			printf("Enet starting in 10BT/FD\n");			priv->duplexity = 1;			priv->speed = 10;			break;		case 3:			printf("Enet starting in 100BT/HD\n");			priv->duplexity = 0;			priv->speed = 100;			break;		case 5:			printf("Enet starting in 100BT/FD\n");			priv->duplexity = 1;			priv->speed = 100;			break;		case 6:			printf("Enet starting in 1000BT/HD\n");			priv->duplexity = 0;			priv->speed = 1000;			break;		case 7:			printf("Enet starting in 1000BT/FD\n");			priv->duplexity = 1;			priv->speed = 1000;			break;		default:			printf("Auto-neg error, defaulting to 10BT/HD\n");			priv->duplexity = 0;			priv->speed = 10;			break;	}	return 0;}/* Parse the 88E1011's status register for speed and duplex * information */uint mii_parse_88E1011_psr(uint mii_reg, struct tsec_private * priv){	uint speed;	mii_reg = read_phy_reg(priv, MIIM_88E1011_PHY_STATUS);	if ((mii_reg & MIIM_88E1011_PHYSTAT_LINK) &&		!(mii_reg & MIIM_88E1011_PHYSTAT_SPDDONE)) {		int i = 0;		puts("Waiting for PHY realtime link");		while (!(mii_reg & MIIM_88E1011_PHYSTAT_SPDDONE)) {			/* Timeout reached ? */

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -