Skip to content
Snippets Groups Projects
smc91111.c 33.9 KiB
Newer Older
  • Learn to ignore specific revisions
  • Wolfgang Denk's avatar
    Wolfgang Denk committed
    
    	/* Final clock bit */
    	bits[clk_idx++] = 0;
    
    	/* Save the current bank */
    
    	oldBank = SMC_inw (dev, BANK_SELECT);
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    
    	/* Select bank 3 */
    
    	SMC_SELECT_BANK (dev, 3);
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    
    	/* Get the current MII register value */
    
    	mii_reg = SMC_inw (dev, MII_REG);
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    
    	/* Turn off all MII Interface bits */
    
    	mii_reg &= ~(MII_MDOE | MII_MCLK | MII_MDI | MII_MDO);
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    
    	/* Clock all 64 cycles */
    
    	for (i = 0; i < sizeof bits; ++i) {
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    		/* Clock Low - output data */
    
    		SMC_outw (dev, mii_reg | bits[i], MII_REG);
    
    		udelay (SMC_PHY_CLOCK_DELAY);
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    
    
    		/* Clock Hi - input data */
    
    		SMC_outw (dev, mii_reg | bits[i] | MII_MCLK, MII_REG);
    
    		udelay (SMC_PHY_CLOCK_DELAY);
    
    		bits[i] |= SMC_inw (dev, MII_REG) & MII_MDI;
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    
    	/* Return to idle state */
    	/* Set clock to low, data to low, and output tristated */
    
    	SMC_outw (dev, mii_reg, MII_REG);
    
    	udelay (SMC_PHY_CLOCK_DELAY);
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    
    	/* Restore original bank select */
    
    	SMC_SELECT_BANK (dev, oldBank);
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    
    	/* Recover input data */
    	phydata = 0;
    
    	for (i = 0; i < 16; ++i) {
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    		phydata <<= 1;
    
    		if (bits[input_idx++] & MII_MDI)
    			phydata |= 0x0001;
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    
    #if (SMC_DEBUG > 2 )
    
    	printf ("smc_read_phy_register(): phyaddr=%x,phyreg=%x,phydata=%x\n",
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    		phyaddr, phyreg, phydata);
    
    	smc_dump_mii_stream (bits, sizeof bits);
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    #endif
    
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    }
    
    
    /*------------------------------------------------------------
     . Writes a register to the MII Management serial interface
     .-------------------------------------------------------------*/
    
    static void smc_write_phy_register (struct eth_device *dev, byte phyreg,
    	word phydata)
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    {
    	int oldBank;
    	int i;
    	word mask;
    	word mii_reg;
    	byte bits[65];
    	int clk_idx = 0;
    	byte phyaddr = SMC_PHY_ADDR;
    
    	/* 32 consecutive ones on MDO to establish sync */
    	for (i = 0; i < 32; ++i)
    		bits[clk_idx++] = MII_MDOE | MII_MDO;
    
    	/* Start code <01> */
    	bits[clk_idx++] = MII_MDOE;
    	bits[clk_idx++] = MII_MDOE | MII_MDO;
    
    	/* Write command <01> */
    	bits[clk_idx++] = MII_MDOE;
    	bits[clk_idx++] = MII_MDOE | MII_MDO;
    
    	/* Output the PHY address, msb first */
    
    	mask = (byte) 0x10;
    	for (i = 0; i < 5; ++i) {
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    		if (phyaddr & mask)
    			bits[clk_idx++] = MII_MDOE | MII_MDO;
    		else
    			bits[clk_idx++] = MII_MDOE;
    
    		/* Shift to next lowest bit */
    		mask >>= 1;
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    
    	/* Output the phy register number, msb first */
    
    	mask = (byte) 0x10;
    	for (i = 0; i < 5; ++i) {
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    		if (phyreg & mask)
    			bits[clk_idx++] = MII_MDOE | MII_MDO;
    		else
    			bits[clk_idx++] = MII_MDOE;
    
    		/* Shift to next lowest bit */
    		mask >>= 1;
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    
    	/* Tristate and turnaround (2 bit times) */
    	bits[clk_idx++] = 0;
    	bits[clk_idx++] = 0;
    
    	/* Write out 16 bits of data, msb first */
    	mask = 0x8000;
    
    	for (i = 0; i < 16; ++i) {
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    		if (phydata & mask)
    			bits[clk_idx++] = MII_MDOE | MII_MDO;
    		else
    			bits[clk_idx++] = MII_MDOE;
    
    		/* Shift to next lowest bit */
    		mask >>= 1;
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    
    	/* Final clock bit (tristate) */
    	bits[clk_idx++] = 0;
    
    	/* Save the current bank */
    
    	oldBank = SMC_inw (dev, BANK_SELECT);
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    
    	/* Select bank 3 */
    
    	SMC_SELECT_BANK (dev, 3);
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    
    	/* Get the current MII register value */
    
    	mii_reg = SMC_inw (dev, MII_REG);
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    
    	/* Turn off all MII Interface bits */
    
    	mii_reg &= ~(MII_MDOE | MII_MCLK | MII_MDI | MII_MDO);
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    
    	/* Clock all cycles */
    
    	for (i = 0; i < sizeof bits; ++i) {
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    		/* Clock Low - output data */
    
    		SMC_outw (dev, mii_reg | bits[i], MII_REG);
    
    		udelay (SMC_PHY_CLOCK_DELAY);
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    
    
    		/* Clock Hi - input data */
    
    		SMC_outw (dev, mii_reg | bits[i] | MII_MCLK, MII_REG);
    
    		udelay (SMC_PHY_CLOCK_DELAY);
    
    		bits[i] |= SMC_inw (dev, MII_REG) & MII_MDI;
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    
    	/* Return to idle state */
    	/* Set clock to low, data to low, and output tristated */
    
    	SMC_outw (dev, mii_reg, MII_REG);
    
    	udelay (SMC_PHY_CLOCK_DELAY);
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    
    	/* Restore original bank select */
    
    	SMC_SELECT_BANK (dev, oldBank);
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    
    #if (SMC_DEBUG > 2 )
    
    	printf ("smc_write_phy_register(): phyaddr=%x,phyreg=%x,phydata=%x\n",
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    		phyaddr, phyreg, phydata);
    
    	smc_dump_mii_stream (bits, sizeof bits);
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    #endif
    }
    #endif /* !CONFIG_SMC91111_EXT_PHY */
    
    
    /*------------------------------------------------------------
     . Configures the specified PHY using Autonegotiation. Calls
     . smc_phy_fixed() if the user has requested a certain config.
     .-------------------------------------------------------------*/
    #ifndef CONFIG_SMC91111_EXT_PHY
    
    static void smc_phy_configure (struct eth_device *dev)
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    {
    	int timeout;
    
    	word my_phy_caps;	/* My PHY capabilities */
    	word my_ad_caps;	/* My Advertised capabilities */
    	word status = 0;	/*;my status = 0 */
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    
    
    	PRINTK3 ("%s: smc_program_phy()\n", SMC_DEV_NAME);
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    
    	/* Reset the PHY, setting all other bits to zero */
    
    	smc_write_phy_register (dev, PHY_CNTL_REG, PHY_CNTL_RST);
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    
    	/* Wait for the reset to complete, or time out */
    
    	timeout = 6;		/* Wait up to 3 seconds */
    	while (timeout--) {
    
    		if (!(smc_read_phy_register (dev, PHY_CNTL_REG)
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    			/* reset complete */
    			break;
    		}
    
    
    		mdelay(500);	/* wait 500 millisecs */
    
    	}
    
    	if (timeout < 1) {
    		printf ("%s:PHY reset timed out\n", SMC_DEV_NAME);
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    		goto smc_phy_configure_exit;
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    
    	/* Read PHY Register 18, Status Output */
    	/* lp->lastPhy18 = smc_read_phy_register(PHY_INT_REG); */
    
    	/* Enable PHY Interrupts (for register 18) */
    	/* Interrupts listed here are disabled */
    
    	smc_write_phy_register (dev, PHY_MASK_REG, 0xffff);
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    
    	/* Configure the Receive/Phy Control register */
    
    	SMC_SELECT_BANK (dev, 0);
    	SMC_outw (dev, RPC_DEFAULT, RPC_REG);
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    
    	/* Copy our capabilities from PHY_STAT_REG to PHY_AD_REG */
    
    	my_phy_caps = smc_read_phy_register (dev, PHY_STAT_REG);
    
    	my_ad_caps = PHY_AD_CSMA;	/* I am CSMA capable */
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    
    	if (my_phy_caps & PHY_STAT_CAP_T4)
    		my_ad_caps |= PHY_AD_T4;
    
    	if (my_phy_caps & PHY_STAT_CAP_TXF)
    		my_ad_caps |= PHY_AD_TX_FDX;
    
    	if (my_phy_caps & PHY_STAT_CAP_TXH)
    		my_ad_caps |= PHY_AD_TX_HDX;
    
    	if (my_phy_caps & PHY_STAT_CAP_TF)
    		my_ad_caps |= PHY_AD_10_FDX;
    
    	if (my_phy_caps & PHY_STAT_CAP_TH)
    		my_ad_caps |= PHY_AD_10_HDX;
    
    	/* Update our Auto-Neg Advertisement Register */
    
    	smc_write_phy_register (dev, PHY_AD_REG, my_ad_caps);
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    
    
    	/* Read the register back.  Without this, it appears that when */
    	/* auto-negotiation is restarted, sometimes it isn't ready and */
    	/* the link does not come up. */
    
    	smc_read_phy_register(dev, PHY_AD_REG);
    
    	PRINTK2 ("%s: phy caps=%x\n", SMC_DEV_NAME, my_phy_caps);
    	PRINTK2 ("%s: phy advertised caps=%x\n", SMC_DEV_NAME, my_ad_caps);
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    
    	/* Restart auto-negotiation process in order to advertise my caps */
    
    	smc_write_phy_register (dev, PHY_CNTL_REG,
    
    				PHY_CNTL_ANEG_EN | PHY_CNTL_ANEG_RST);
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    
    	/* Wait for the auto-negotiation to complete.  This may take from */
    	/* 2 to 3 seconds. */
    	/* Wait for the reset to complete, or time out */
    
    	timeout = CONFIG_SMC_AUTONEG_TIMEOUT * 2;
    
    		status = smc_read_phy_register (dev, PHY_STAT_REG);
    
    		if (status & PHY_STAT_ANEG_ACK) {
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    			/* auto-negotiate complete */
    			break;
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    
    
    		mdelay(500);	/* wait 500 millisecs */
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    
    		/* Restart auto-negotiation if remote fault */
    
    		if (status & PHY_STAT_REM_FLT) {
    
    			printf ("%s: PHY remote fault detected\n",
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    
    			/* Restart auto-negotiation */
    
    			printf ("%s: PHY restarting auto-negotiation\n",
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    				SMC_DEV_NAME);
    
    			smc_write_phy_register (dev, PHY_CNTL_REG,
    
    						PHY_CNTL_ANEG_EN |
    						PHY_CNTL_ANEG_RST |
    						PHY_CNTL_SPEED |
    						PHY_CNTL_DPLX);
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    		}
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    
    
    		printf ("%s: PHY auto-negotiate timed out\n", SMC_DEV_NAME);
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    
    	/* Fail if we detected an auto-negotiate remote fault */
    
    	if (status & PHY_STAT_REM_FLT) {
    
    		printf ("%s: PHY remote fault detected\n", SMC_DEV_NAME);
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    
    	/* Re-Configure the Receive/Phy Control register */
    
    	SMC_outw (dev, RPC_DEFAULT, RPC_REG);
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    
    
    smc_phy_configure_exit:	;
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    
    }
    #endif /* !CONFIG_SMC91111_EXT_PHY */
    
    
    #if SMC_DEBUG > 2
    static void print_packet( byte * buf, int length )
    {
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    	int i;
    	int remainder;
    	int lines;
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    	printf("Packet of length %d \n", length );
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    
    #if SMC_DEBUG > 3
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    	lines = length / 16;
    	remainder = length % 16;
    
    	for ( i = 0; i < lines ; i ++ ) {
    		int cur;
    
    		for ( cur = 0; cur < 8; cur ++ ) {
    			byte a, b;
    
    			a = *(buf ++ );
    			b = *(buf ++ );
    			printf("%02x%02x ", a, b );
    		}
    		printf("\n");
    	}
    	for ( i = 0; i < remainder/2 ; i++ ) {
    		byte a, b;
    
    		a = *(buf ++ );
    		b = *(buf ++ );
    		printf("%02x%02x ", a, b );
    	}
    	printf("\n");
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    #endif
    }
    #endif
    
    
    int smc91111_initialize(u8 dev_num, int base_addr)
    
    	struct smc91111_priv *priv;
    	struct eth_device *dev;
    	int i;
    
    	priv = malloc(sizeof(*priv));
    	if (!priv)
    		return 0;
    	dev = malloc(sizeof(*dev));
    	if (!dev) {
    		free(priv);
    		return 0;
    
    Thomas Chou's avatar
    Thomas Chou committed
    	memset(dev, 0, sizeof(*dev));
    
    	priv->dev_num = dev_num;
    	dev->priv = priv;
    	dev->iobase = base_addr;
    
    	swap_to(ETHERNET);
    	SMC_SELECT_BANK(dev, 1);
    	for (i = 0; i < 6; ++i)
    		dev->enetaddr[i] = SMC_inb(dev, (ADDR0_REG + i));
    	swap_to(FLASH);
    
    	dev->init = smc_init;
    	dev->halt = smc_halt;
    	dev->send = smc_send;
    	dev->recv = smc_rcv;
    
    Thomas Chou's avatar
    Thomas Chou committed
    	dev->write_hwaddr = smc_write_hwaddr;
    
    	sprintf(dev->name, "%s-%hu", SMC_DEV_NAME, dev_num);
    
    	eth_register(dev);
    	return 0;