Skip to content
Snippets Groups Projects
tsec.c 47.1 KiB
Newer Older
  • Learn to ignore specific revisions
  •  * 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-2009 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>
    
    #include <tsec.h>
    
    #include "miiphy.h"
    
    #define TX_BUF_CNT		2
    
    static 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];
    
    #define MAXCONTROLLERS	(8)
    
    
    static struct tsec_private *privlist[MAXCONTROLLERS];
    
    static int num_tsecs = 0;
    
    #ifdef __GNUC__
    static RTXBD rtx __attribute__ ((aligned(8)));
    #else
    #error "rtx must be 64-bit aligned"
    #endif
    
    
    static 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);
    
    #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);
    
    David Updegraff's avatar
    David Updegraff committed
    #ifdef CONFIG_MCAST_TFTP
    static int tsec_mcast_addr (struct eth_device *dev, u8 mcast_mac, u8 set);
    #endif
    
    /* Default initializations for TSEC controllers. */
    
    static struct tsec_info_struct tsec_info[] = {
    #ifdef CONFIG_TSEC1
    	STD_TSEC_INFO(1),	/* TSEC1 */
    #endif
    #ifdef CONFIG_TSEC2
    	STD_TSEC_INFO(2),	/* TSEC2 */
    #endif
    #ifdef CONFIG_MPC85XX_FEC
    	{
    		.regs = (tsec_t *)(TSEC_BASE_ADDR + 0x2000),
    
    		.miiregs = (tsec_mdio_t *)(MDIO_BASE_ADDR),
    
    		.devname = CONFIG_MPC85XX_FEC_NAME,
    		.phyaddr = FEC_PHY_ADDR,
    		.flags = FEC_FLAGS
    	},			/* FEC */
    #endif
    #ifdef CONFIG_TSEC3
    	STD_TSEC_INFO(3),	/* TSEC3 */
    #endif
    #ifdef CONFIG_TSEC4
    	STD_TSEC_INFO(4),	/* TSEC4 */
    #endif
    };
    
    int tsec_eth_init(bd_t *bis, struct tsec_info_struct *tsecs, int num)
    {
    	int i;
    
    	for (i = 0; i < num; i++)
    		tsec_initialize(bis, &tsecs[i]);
    
    	return 0;
    }
    
    int tsec_standard_init(bd_t *bis)
    {
    	return tsec_eth_init(bis, tsec_info, ARRAY_SIZE(tsec_info));
    }
    
    
    /* Initialize device structure. Returns success if PHY
     * initialization succeeded (i.e. if it recognizes the PHY)
     */
    
    int tsec_initialize(bd_t * bis, struct tsec_info_struct *tsec_info)
    
    	struct eth_device *dev;
    
    	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)
    
    	privlist[num_tsecs++] = priv;
    	priv->regs = tsec_info->regs;
    	priv->phyregs = tsec_info->miiregs;
    
    	priv->phyregs_sgmii = tsec_info->miiregs_sgmii;
    
    	priv->phyaddr = tsec_info->phyaddr;
    	priv->flags = tsec_info->flags;
    
    	sprintf(dev->name, tsec_info->devname);
    
    	dev->iobase = 0;
    
    	dev->priv = priv;
    	dev->init = tsec_init;
    	dev->halt = tsec_halt;
    	dev->send = tsec_send;
    	dev->recv = tsec_recv;
    
    David Updegraff's avatar
    David Updegraff committed
    #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;
    
    	udelay(2);  /* Soft Reset must be asserted for 3 TX clocks */
    
    	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];
    
    	tempval = (tmpbuf[0] << 24) | (tmpbuf[1] << 16) | (tmpbuf[2] << 8) |
    		  tmpbuf[3];
    
    	regs->macstnaddr1 = tempval;
    
    	tempval = *((uint *) (tmpbuf + 4));
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    	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 ? 0 : -1);
    
    /* Writes the given phy's reg with value, using the specified MDIO regs */
    
    static void tsec_local_mdio_write(volatile tsec_mdio_t *phyregs, uint addr,
    
    		uint reg, uint value)
    
    	int timeout = 1000000;
    
    	phyregs->miimadd = (addr << 8) | reg;
    	phyregs->miimcon = value;
    
    Eran Liberty's avatar
    Eran Liberty committed
    	asm("sync");
    
    	timeout = 1000000;
    
    	while ((phyregs->miimind & MIIMIND_BUSY) && timeout--) ;
    
    
    /* Provide the default behavior of writing the PHY of this ethernet device */
    #define write_phy_reg(priv, regnum, value) tsec_local_mdio_write(priv->phyregs,priv->phyaddr,regnum,value)
    
    /* Reads register regnum on the device's PHY through the
    
     * specified registers.	 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 tsec_local_mdio_read(volatile tsec_mdio_t *phyregs, uint phyid, uint regnum)
    
    	/* Put the address of the phy, and the register
    	 * number into MIIMADD */
    
    	phyregs->miimadd = (phyid << 8) | regnum;
    
    
    	/* Clear the command register, and wait */
    
    	phyregs->miimcom = 0;
    
    Eran Liberty's avatar
    Eran Liberty committed
    	asm("sync");
    
    
    	/* Initiate a read command, and wait */
    
    	phyregs->miimcom = MIIM_READ_COMMAND;
    
    Eran Liberty's avatar
    Eran Liberty committed
    	asm("sync");
    
    
    	/* Wait for the the indication that the read is done */
    
    	while ((phyregs->miimind & (MIIMIND_NOTVALID | MIIMIND_BUSY))) ;
    
    
    	/* Grab the value read from the PHY */
    
    	value = phyregs->miimstat;
    
    /* #define to provide old read_phy_reg functionality without duplicating code */
    
    #define read_phy_reg(priv,regnum) tsec_local_mdio_read(priv->phyregs,priv->phyaddr,regnum)
    
    #define TBIANA_SETTINGS ( \
    		TBIANA_ASYMMETRIC_PAUSE \
    		| TBIANA_SYMMETRIC_PAUSE \
    		| TBIANA_FULL_DUPLEX \
    		)
    
    #define TBICR_SETTINGS ( \
    		TBICR_PHY_RESET \
    		| TBICR_ANEG_ENABLE \
    		| TBICR_FULL_DUPLEX \
    		| TBICR_SPEED1_SET \
    		)
    /* Configure the TBI for SGMII operation */
    static void tsec_configure_serdes(struct tsec_private *priv)
    {
    
    	/* Access TBI PHY registers at given TSEC register offset as opposed to the
    	 * register offset used for external PHY accesses */
    
    	tsec_local_mdio_write(priv->phyregs_sgmii, priv->regs->tbipa, TBI_ANA,
    
    			TBIANA_SETTINGS);
    
    	tsec_local_mdio_write(priv->phyregs_sgmii, priv->regs->tbipa, TBI_TBICON,
    
    			TBICON_CLK_SELECT);
    
    	tsec_local_mdio_write(priv->phyregs_sgmii, priv->regs->tbipa, TBI_CR,
    
    			TBICR_SETTINGS);
    }
    
    /* 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 = priv->regs;
    
    
    	/* Assign a Physical address to the TBI */
    
    	regs->tbipa = CONFIG_SYS_TBIPA_VALUE;
    
    
    	/* Reset MII (due to new addresses) */
    	priv->phyregs->miimcfg = MIIMCFG_RESET;
    
    Eran Liberty's avatar
    Eran Liberty committed
    	asm("sync");
    
    	priv->phyregs->miimcfg = MIIMCFG_INIT_VALUE;
    
    Eran Liberty's avatar
    Eran Liberty committed
    	asm("sync");
    
    	while (priv->phyregs->miimind & MIIMIND_BUSY) ;
    
    	/* 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);
    
    	if (regs->ecntrl & ECNTRL_SGMII_MODE)
    		tsec_configure_serdes(priv);
    
    
    	priv->phyinfo = curphy;
    
    	phy_run_commands(priv, priv->phyinfo->config);
    
    /*
     * 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;
    
    		return MIIM_CR_INIT;
    }
    
    /*
     * Wait for auto-negotiation to complete, then determine link
    
     */
    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 & PHY_BMSR_AUTN_ABLE) && !(mii_reg & PHY_BMSR_AUTN_COMP)) {
    
    		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 (ctrlc()) {
    				puts("user interrupt!\n");
    				priv->link = 0;
    				return -EINTR;
    			}
    
    
    			if ((i++ % 1000) == 0) {
    
    			udelay(1000);	/* 1 ms */
    
    			mii_reg = read_phy_reg(priv, MIIM_STATUS);
    
    		puts(" done\n");
    
    
    		/* Link status bit is latched low, read it again */
    		mii_reg = read_phy_reg(priv, MIIM_STATUS);
    
    
    		udelay(500000);	/* another 500 ms (results in faster booting) */
    
    	priv->link = mii_reg & MIIM_STATUS_LINK ? 1 : 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;
    }
    
    
    /*
     * "Ethernet@Wirespeed" needs to be enabled to achieve link in certain
     * circumstances.  eg a gigabit TSEC connected to a gigabit switch with
     * a 4-wire ethernet cable.  Both ends advertise gigabit, but can't
     * link.  "Ethernet@Wirespeed" reduces advertised speed until link
     * can be achieved.
     */
    uint mii_BCM54xx_wirespeed(uint mii_reg, struct tsec_private *priv)
    {
    	return (read_phy_reg(priv, mii_reg) & 0x8FFF) | 0x8010;
    }
    
    
    /*
     * 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)
    
    	mii_reg = read_phy_reg(priv, MIIM_88E1011_PHY_STATUS);
    
    
    	if ((mii_reg & MIIM_88E1011_PHYSTAT_LINK) &&
    		!(mii_reg & MIIM_88E1011_PHYSTAT_SPDDONE)) {
    
    		puts("Waiting for PHY realtime link");
    
    		while (!(mii_reg & MIIM_88E1011_PHYSTAT_SPDDONE)) {
    			/* Timeout reached ? */
    
    			if (i > PHY_AUTONEGOTIATE_TIMEOUT) {
    
    				puts(" TIMEOUT !\n");
    
    				priv->link = 0;
    				break;
    			}
    
    			if ((i++ % 1000) == 0) {
    
    			udelay(1000);	/* 1 ms */
    
    			mii_reg = read_phy_reg(priv, MIIM_88E1011_PHY_STATUS);
    		}
    
    		puts(" done\n");
    		udelay(500000);	/* another 500 ms (results in faster booting) */
    
    	} else {
    		if (mii_reg & MIIM_88E1011_PHYSTAT_LINK)
    			priv->link = 1;
    		else
    			priv->link = 0;
    
    	if (mii_reg & MIIM_88E1011_PHYSTAT_DUPLEX)
    
    		priv->duplexity = 1;
    	else
    		priv->duplexity = 0;
    
    
    	speed = (mii_reg & MIIM_88E1011_PHYSTAT_SPEED);
    
    	switch (speed) {
    	case MIIM_88E1011_PHYSTAT_GBIT:
    		priv->speed = 1000;
    		break;
    	case MIIM_88E1011_PHYSTAT_100:
    		priv->speed = 100;
    		break;
    	default:
    		priv->speed = 10;
    
    /* Parse the RTL8211B's status register for speed and duplex
     * information
     */
    uint mii_parse_RTL8211B_sr(uint mii_reg, struct tsec_private * priv)
    {
    	uint speed;
    
    	mii_reg = read_phy_reg(priv, MIIM_RTL8211B_PHY_STATUS);
    
    	if (!(mii_reg & MIIM_RTL8211B_PHYSTAT_SPDDONE)) {
    
    		/* in case of timeout ->link is cleared */
    		priv->link = 1;
    
    		puts("Waiting for PHY realtime link");
    		while (!(mii_reg & MIIM_RTL8211B_PHYSTAT_SPDDONE)) {
    			/* Timeout reached ? */
    			if (i > PHY_AUTONEGOTIATE_TIMEOUT) {
    				puts(" TIMEOUT !\n");
    				priv->link = 0;
    				break;
    			}
    
    			if ((i++ % 1000) == 0) {
    				putc('.');
    			}
    			udelay(1000);	/* 1 ms */
    			mii_reg = read_phy_reg(priv, MIIM_RTL8211B_PHY_STATUS);
    		}
    		puts(" done\n");
    		udelay(500000);	/* another 500 ms (results in faster booting) */
    	} else {
    		if (mii_reg & MIIM_RTL8211B_PHYSTAT_LINK)
    			priv->link = 1;
    		else
    			priv->link = 0;
    	}
    
    	if (mii_reg & MIIM_RTL8211B_PHYSTAT_DUPLEX)
    		priv->duplexity = 1;
    	else
    		priv->duplexity = 0;
    
    	speed = (mii_reg & MIIM_RTL8211B_PHYSTAT_SPEED);
    
    	switch (speed) {
    	case MIIM_RTL8211B_PHYSTAT_GBIT:
    		priv->speed = 1000;
    		break;
    	case MIIM_RTL8211B_PHYSTAT_100:
    		priv->speed = 100;
    		break;
    	default:
    		priv->speed = 10;
    	}
    
    	return 0;
    }
    
    
    /* Parse the cis8201's status register for speed and duplex
    
     * information
     */
    uint mii_parse_cis8201(uint mii_reg, struct tsec_private * priv)
    
    	if (mii_reg & MIIM_CIS8201_AUXCONSTAT_DUPLEX)
    
    		priv->duplexity = 1;
    	else
    		priv->duplexity = 0;
    
    	speed = mii_reg & MIIM_CIS8201_AUXCONSTAT_SPEED;
    
    	switch (speed) {
    	case MIIM_CIS8201_AUXCONSTAT_GBIT:
    		priv->speed = 1000;
    		break;
    	case MIIM_CIS8201_AUXCONSTAT_100:
    		priv->speed = 100;
    		break;
    	default:
    		priv->speed = 10;
    		break;
    
    /* Parse the vsc8244's status register for speed and duplex
    
     * information
     */
    uint mii_parse_vsc8244(uint mii_reg, struct tsec_private * priv)
    
    	if (mii_reg & MIIM_VSC8244_AUXCONSTAT_DUPLEX)
    		priv->duplexity = 1;
    	else
    		priv->duplexity = 0;
    
    	speed = mii_reg & MIIM_VSC8244_AUXCONSTAT_SPEED;
    	switch (speed) {
    	case MIIM_VSC8244_AUXCONSTAT_GBIT:
    		priv->speed = 1000;
    		break;
    	case MIIM_VSC8244_AUXCONSTAT_100:
    		priv->speed = 100;
    		break;
    	default:
    		priv->speed = 10;
    		break;
    	}
    
    	return 0;
    }
    
    
    /* Parse the DM9161's status register for speed and duplex
    
     * information
     */
    uint mii_parse_dm9161_scsr(uint mii_reg, struct tsec_private * priv)
    
    	if (mii_reg & (MIIM_DM9161_SCSR_100F | MIIM_DM9161_SCSR_100H))
    
    		priv->speed = 100;
    	else
    		priv->speed = 10;
    
    
    	if (mii_reg & (MIIM_DM9161_SCSR_100F | MIIM_DM9161_SCSR_10F))
    
    		priv->duplexity = 1;
    	else
    		priv->duplexity = 0;
    
    	return 0;
    }
    
    
    /*
     * Hack to write all 4 PHYs with the LED values
     */
    uint mii_cis8204_fixled(uint mii_reg, struct tsec_private * priv)
    
    	volatile tsec_mdio_t *regbase = priv->phyregs;
    
    	int timeout = 1000000;
    
    	for (phyid = 0; phyid < 4; phyid++) {
    
    		regbase->miimadd = (phyid << 8) | mii_reg;
    		regbase->miimcon = MIIM_CIS8204_SLEDCON_INIT;
    
    Eran Liberty's avatar
    Eran Liberty committed
    		asm("sync");
    
    		timeout = 1000000;
    		while ((regbase->miimind & MIIMIND_BUSY) && timeout--) ;
    
    	return MIIM_CIS8204_SLEDCON_INIT;
    
    uint mii_cis8204_setmode(uint mii_reg, struct tsec_private * priv)
    
    {
    	if (priv->flags & TSEC_REDUCED)
    		return MIIM_CIS8204_EPHYCON_INIT | MIIM_CIS8204_EPHYCON_RGMII;
    	else
    		return MIIM_CIS8204_EPHYCON_INIT;
    }
    
    uint mii_m88e1111s_setmode(uint mii_reg, struct tsec_private *priv)
    {
    	uint mii_data = read_phy_reg(priv, mii_reg);
    
    	if (priv->flags & TSEC_REDUCED)
    		mii_data = (mii_data & 0xfff0) | 0x000b;
    	return mii_data;
    }
    
    
    /* Initialized required registers to appropriate values, zeroing
     * those we don't care about (unless zero is bad, in which case,
    
     * choose a more appropriate value)
     */
    static void init_registers(volatile tsec_t * regs)
    
    {
    	/* Clear IEVENT */
    	regs->ievent = IEVENT_INIT_CLEAR;
    
    	regs->imask = IMASK_INIT_CLEAR;
    
    	regs->hash.iaddr0 = 0;
    	regs->hash.iaddr1 = 0;
    	regs->hash.iaddr2 = 0;
    	regs->hash.iaddr3 = 0;
    	regs->hash.iaddr4 = 0;
    	regs->hash.iaddr5 = 0;
    	regs->hash.iaddr6 = 0;
    	regs->hash.iaddr7 = 0;
    
    	regs->hash.gaddr0 = 0;
    	regs->hash.gaddr1 = 0;
    	regs->hash.gaddr2 = 0;
    	regs->hash.gaddr3 = 0;
    	regs->hash.gaddr4 = 0;
    	regs->hash.gaddr5 = 0;
    	regs->hash.gaddr6 = 0;
    	regs->hash.gaddr7 = 0;
    
    	regs->rctrl = 0x00000000;
    
    	/* Init RMON mib registers */
    	memset((void *)&(regs->rmon), 0, sizeof(rmon_mib_t));
    
    	regs->rmon.cam1 = 0xffffffff;
    	regs->rmon.cam2 = 0xffffffff;
    
    	regs->mrblr = MRBLR_INIT_SETTINGS;
    
    	regs->minflr = MINFLR_INIT_SETTINGS;
    
    	regs->attr = ATTR_INIT_SETTINGS;
    	regs->attreli = ATTRELI_INIT_SETTINGS;
    
    }
    
    
    /* Configure maccfg2 based on negotiated speed and duplex
    
     * reported by PHY handling code
     */
    
    static void adjust_link(struct eth_device *dev)
    {
    	struct tsec_private *priv = (struct tsec_private *)dev->priv;
    	volatile tsec_t *regs = priv->regs;
    
    
    	if (priv->link) {
    		if (priv->duplexity != 0)
    
    			regs->maccfg2 |= MACCFG2_FULL_DUPLEX;
    		else
    			regs->maccfg2 &= ~(MACCFG2_FULL_DUPLEX);
    
    
    		switch (priv->speed) {
    		case 1000:
    			regs->maccfg2 = ((regs->maccfg2 & ~(MACCFG2_IF))
    					 | MACCFG2_GMII);
    			break;
    		case 100:
    		case 10:
    			regs->maccfg2 = ((regs->maccfg2 & ~(MACCFG2_IF))
    					 | MACCFG2_MII);
    
    
    			/* Set R100 bit in all modes although
    			 * it is only used in RGMII mode
    
    			if (priv->speed == 100)
    
    				regs->ecntrl |= ECNTRL_R100;
    			else
    				regs->ecntrl &= ~(ECNTRL_R100);
    			break;
    		default:
    			printf("%s: Speed was bad\n", dev->name);
    			break;
    
    		}
    
    		printf("Speed: %d, %s duplex\n", priv->speed,
    
    		       (priv->duplexity) ? "full" : "half");
    
    
    	} else {
    		printf("%s: No link.\n", dev->name);
    	}
    }
    
    /* Set up the buffers and their descriptors, and bring up the
    
    static void startup_tsec(struct eth_device *dev)
    
    	struct tsec_private *priv = (struct tsec_private *)dev->priv;
    	volatile tsec_t *regs = priv->regs;
    
    
    	/* Point to the buffer descriptors */
    	regs->tbase = (unsigned int)(&rtx.txbd[txIdx]);
    	regs->rbase = (unsigned int)(&rtx.rxbd[rxIdx]);
    
    	/* Initialize the Rx Buffer descriptors */
    	for (i = 0; i < PKTBUFSRX; i++) {
    		rtx.rxbd[i].status = RXBD_EMPTY;
    		rtx.rxbd[i].length = 0;
    
    		rtx.rxbd[i].bufPtr = (uint) NetRxPackets[i];
    
    	rtx.rxbd[PKTBUFSRX - 1].status |= RXBD_WRAP;
    
    
    	/* Initialize the TX Buffer Descriptors */
    
    	for (i = 0; i < TX_BUF_CNT; i++) {
    
    		rtx.txbd[i].status = 0;
    		rtx.txbd[i].length = 0;
    		rtx.txbd[i].bufPtr = 0;
    	}
    
    	rtx.txbd[TX_BUF_CNT - 1].status |= TXBD_WRAP;
    
    	/* Start up the PHY */
    
    	if(priv->phyinfo)
    		phy_run_commands(priv, priv->phyinfo->startup);
    
    	/* Enable Transmit and Receive */
    	regs->maccfg1 |= (MACCFG1_RX_EN | MACCFG1_TX_EN);
    
    	/* Tell the DMA it is clear to go */
    	regs->dmactrl |= DMACTRL_INIT_SETTINGS;
    	regs->tstat = TSTAT_CLEAR_THALT;
    
    	regs->rstat = RSTAT_CLEAR_RHALT;
    
    	regs->dmactrl &= ~(DMACTRL_GRS | DMACTRL_GTS);
    }
    
    
    /* This returns the status bits of the device.	The return value
    
     * is never checked, and this is what the 8260 driver did, so we
    
     * do the same.	 Presumably, this would be zero if there were no
    
     * errors
     */
    static int tsec_send(struct eth_device *dev, volatile void *packet, int length)
    
    {
    	int i;
    	int result = 0;
    
    	struct tsec_private *priv = (struct tsec_private *)dev->priv;
    	volatile tsec_t *regs = priv->regs;
    
    
    	/* Find an empty buffer descriptor */
    
    	for (i = 0; rtx.txbd[txIdx].status & TXBD_READY; i++) {
    
    		if (i >= TOUT_LOOP) {
    
    			debug("%s: tsec: tx buffers full\n", dev->name);
    
    	rtx.txbd[txIdx].bufPtr = (uint) packet;
    
    	rtx.txbd[txIdx].length = length;
    
    	rtx.txbd[txIdx].status |=
    	    (TXBD_READY | TXBD_LAST | TXBD_CRC | TXBD_INTERRUPT);
    
    
    	/* Tell the DMA to go */
    	regs->tstat = TSTAT_CLEAR_THALT;
    
    	/* Wait for buffer to be transmitted */
    
    	for (i = 0; rtx.txbd[txIdx].status & TXBD_READY; i++) {
    
    		if (i >= TOUT_LOOP) {
    
    			debug("%s: tsec: tx error\n", dev->name);
    
    			return result;
    		}
    	}
    
    	txIdx = (txIdx + 1) % TX_BUF_CNT;
    	result = rtx.txbd[txIdx].status & TXBD_STATS;
    
    	return result;
    }
    
    
    static int tsec_recv(struct eth_device *dev)
    
    	struct tsec_private *priv = (struct tsec_private *)dev->priv;
    	volatile tsec_t *regs = priv->regs;
    
    	while (!(rtx.rxbd[rxIdx].status & RXBD_EMPTY)) {
    
    
    		length = rtx.rxbd[rxIdx].length;
    
    		/* Send the packet up if there were no errors */
    		if (!(rtx.rxbd[rxIdx].status & RXBD_STATS)) {
    			NetReceive(NetRxPackets[rxIdx], length - 4);
    
    		} else {
    			printf("Got error %x\n",
    
    			       (rtx.rxbd[rxIdx].status & RXBD_STATS));
    
    		}
    
    		rtx.rxbd[rxIdx].length = 0;
    
    		/* Set the wrap bit if this is the last element in the list */
    
    		rtx.rxbd[rxIdx].status =
    		    RXBD_EMPTY | (((rxIdx + 1) == PKTBUFSRX) ? RXBD_WRAP : 0);
    
    
    		rxIdx = (rxIdx + 1) % PKTBUFSRX;
    	}
    
    
    	if (regs->ievent & IEVENT_BSY) {
    
    		regs->ievent = IEVENT_BSY;
    		regs->rstat = RSTAT_CLEAR_RHALT;
    	}
    
    	return -1;
    
    }
    
    
    /* Stop the interface */
    
    static void tsec_halt(struct eth_device *dev)
    
    	struct tsec_private *priv = (struct tsec_private *)dev->priv;
    	volatile tsec_t *regs = priv->regs;
    
    
    	regs->dmactrl &= ~(DMACTRL_GRS | DMACTRL_GTS);
    	regs->dmactrl |= (DMACTRL_GRS | DMACTRL_GTS);
    
    
    	while (!(regs->ievent & (IEVENT_GRSC | IEVENT_GTSC))) ;
    
    
    	regs->maccfg1 &= ~(MACCFG1_TX_EN | MACCFG1_RX_EN);
    
    
    	/* Shut down the PHY, as needed */
    
    	if(priv->phyinfo)
    		phy_run_commands(priv, priv->phyinfo->shutdown);
    
    struct phy_info phy_info_M88E1149S = {