Source to bsd/dev/ppc/drvBMacEnet/BMacEnetMII.m


Enter a symbol's name here to quickly find it.

/*
 * Copyright (c) 1999 Apple Computer, Inc. All rights reserved.
 *
 * @APPLE_LICENSE_HEADER_START@
 * 
 * "Portions Copyright (c) 1999 Apple Computer, Inc.  All Rights
 * Reserved.  This file contains Original Code and/or Modifications of
 * Original Code as defined in and that are subject to the Apple Public
 * Source License Version 1.0 (the 'License').  You may not use this file
 * except in compliance with the License.  Please obtain a copy of the
 * License at http://www.apple.com/publicsource and read it before using
 * this file.
 * 
 * The Original Code and all software distributed under the License are
 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT.  Please see the
 * License for the specific language governing rights and limitations
 * under the License."
 * 
 * @APPLE_LICENSE_HEADER_END@
 */

/*
 * Copyright (c) 1998-1999 by Apple Computer, Inc., All rights reserved.
 *
 * MII/PHY (National Semiconductor DP83840/DP83840A) support methods.
 * It is general enough to work with most MII/PHYs.
 *
 * HISTORY
 *
 */
#import "BMacEnetPrivate.h"

@implementation BMacEnet(MII)

/*
 * Read from MII/PHY registers.
 */
- (BOOL)miiReadWord:(unsigned short *)dataPtr reg:(unsigned short)reg
	phy:(unsigned char)phy
{
    int					i;
    miiFrameUnion 		frame;
    unsigned short		phyreg;
    BOOL			ret = YES;

    do
    {
        // Write preamble
        //
        [self miiWrite:MII_FRAME_PREAMBLE size:MII_FRAME_SIZE];

	
        if ([self miiCheckZeroBit] == YES) 
        {
//          IOLog("Ethernet(BMac): MII not floating before read\n\r");
	    ret = NO;
            break;
        }

        // Prepare command frame
        //
        frame.data = MII_FRAME_READ;
        frame.bit.regad = reg;
        frame.bit.phyad = phy;
	
        // write ST, OP, PHYAD, REGAD in the MII command frame
        //
        [self miiWrite:frame.data size:14];
	
        // Hi-Z state
        // Make sure the PHY generated a zero bit after the 2nd Hi-Z bit
        //

        [self miiOutThreeState];

        if ([self miiCheckZeroBit] == NO) 
        {
//          IOLog("Ethernet(BMac): MII not driven after turnaround\n\r");
	    ret = NO;
            break;
        }

        // read 16-bit data
        //
        phyreg = 0;
        for (i = 0; i < 16; i++) 
        {
	    phyreg = [self miiReadBit] | (phyreg << 1);
        }
        if (dataPtr)
	    *dataPtr = phyreg;

        // Hi-Z state
        [self miiOutThreeState];
	
        if ([self miiCheckZeroBit] == YES) 
        {
//          IOLog("Ethernet(BMac): MII not floating after read\n\r");
	    ret = NO;
            break;
        }
    }
    while ( 0 );

    return ret;
}

/*
 * Write to MII/PHY registers.
 */
- (BOOL)miiWriteWord:(unsigned short)data reg:(unsigned short)reg
	phy:(unsigned char)phy
{
    miiFrameUnion 		frame;
    BOOL			ret = YES;
	
    do
    {
        // Write preamble
        //
        [self miiWrite:MII_FRAME_PREAMBLE size:MII_FRAME_SIZE];

        if ([self miiCheckZeroBit] == YES) 
        {
	    ret = NO;
            break;
        }

        // Prepare command frame
        //
        frame.data = MII_FRAME_WRITE;
        frame.bit.regad = reg;
        frame.bit.phyad = phy;
        frame.bit.data  = data;
	
        // Write command frame
        //
        [self miiWrite:frame.data size:MII_FRAME_SIZE];

        // Hi-Z state
        [self miiOutThreeState];

        if ([self miiCheckZeroBit] == YES) 
        {
	    ret = NO;
            break;
        }
    }
    while ( 0 );

    return ret;
}

/* 
 * Write 'dataSize' number of bits to the MII management interface,
 * starting with the most significant bit of 'miiData'.
 *
 */
- (void)miiWrite:(unsigned int)miiData size:(unsigned int)dataSize
{
    int i;
    u_int16_t	regValue;
	
    regValue = kMIFCSR_DataOutEnable;
		
    for (i = dataSize; i > 0; i--) 
    {
        int bit = ((miiData & 0x80000000) ? kMIFCSR_DataOut : 0);
		
        regValue &= ~(kMIFCSR_Clock | kMIFCSR_DataOut) ;
        regValue |=  bit;
	WriteBigMacRegister(ioBaseEnet, kMIFCSR, regValue);
	IODelay(phyMIIDelay);
		
	regValue |= kMIFCSR_Clock;
	WriteBigMacRegister(ioBaseEnet, kMIFCSR, regValue );
	IODelay(phyMIIDelay);

	miiData = miiData << 1;
    }
}

/*
 * Read one bit from the MII management interface.
 */
- (int)miiReadBit
{
    u_int16_t		regValue;
    u_int16_t		regValueRead;

    regValue = 0;	

    WriteBigMacRegister(ioBaseEnet, kMIFCSR, regValue);
    IODelay(phyMIIDelay);

    regValue |= kMIFCSR_Clock;
    WriteBigMacRegister(ioBaseEnet, kMIFCSR, regValue);
    IODelay(phyMIIDelay);
	
    regValueRead = ReadBigMacRegister(ioBaseEnet, kMIFCSR);
    IODelay(phyMIIDelay);	// delay next invocation of this routine
	
    return ( (regValueRead & kMIFCSR_DataIn) ? 1 : 0 );
}

/*
 * Read the zero bit on the second clock of the turn-around (TA)
 * when reading a PHY register.
 */
- (BOOL)miiCheckZeroBit
{
    u_int16_t	regValue;
	
    regValue = ReadBigMacRegister(ioBaseEnet, kMIFCSR);
    
    return (((regValue & kMIFCSR_DataIn) == 0) ? YES : NO );
}

/*
 * Tri-state the STA's MDIO pin.
 */
- (void)miiOutThreeState
{
    u_int16_t		regValue;

    regValue = 0;	
    WriteBigMacRegister(ioBaseEnet, kMIFCSR, regValue);
    IODelay(phyMIIDelay);
	
    regValue |= kMIFCSR_Clock;
    WriteBigMacRegister(ioBaseEnet, kMIFCSR, regValue);
    IODelay(phyMIIDelay);
}

- (BOOL)miiResetPHY:(unsigned char)phy
{
    int i = MII_RESET_TIMEOUT;
    unsigned short mii_control;

    // Set the reset bit
    //
    [self miiWriteWord:MII_CONTROL_RESET reg:MII_CONTROL phy:phy];
	
    IOSleep(MII_RESET_DELAY);

    // Wait till reset process is complete (MII_CONTROL_RESET returns to zero)
    //
    while (i > 0) 
    {
	if ([self miiReadWord:&mii_control reg:MII_CONTROL phy:phy] == NO)
		return NO;

	if (!(mii_control & MII_CONTROL_RESET))
        {
            [self miiReadWord:&mii_control reg:MII_CONTROL phy:phy];
            mii_control &= ~MII_CONTROL_ISOLATE;
            [self miiWriteWord:mii_control reg:MII_CONTROL phy:phy];
            return YES;
        }

	IOSleep(MII_RESET_DELAY);
	i -= MII_RESET_DELAY;
    }
    return NO;
}

- (BOOL)miiWaitForLink:(unsigned char)phy
{
    int i = MII_LINK_TIMEOUT;
    unsigned short mii_status;
	
    while (i > 0) 
    {
	if ([self miiReadWord:&mii_status reg:MII_STATUS phy:phy] == NO)
		return NO;
		
	if (mii_status & MII_STATUS_LINK_STATUS)
		return YES;
		
	IOSleep(MII_LINK_DELAY);
	i -= MII_LINK_DELAY;
    }
    return NO;
}

- (BOOL)miiWaitForAutoNegotiation:(unsigned char)phy
{
    int i = MII_LINK_TIMEOUT;
    unsigned short mii_status;
	
    while (i > 0) 
    {
	if ([self miiReadWord:&mii_status reg:MII_STATUS phy:phy] == NO)
		return NO;
		
	if (mii_status & MII_STATUS_NEGOTIATION_COMPLETE)
		return YES;
		
	IOSleep(MII_LINK_DELAY);
	i -= MII_LINK_DELAY;
    }
    return NO;
}

- (void)miiRestartAutoNegotiation:(unsigned char)phy
{
    unsigned short mii_control;

    [self miiReadWord:&mii_control reg:MII_CONTROL phy:phy];
    mii_control |= MII_CONTROL_RESTART_NEGOTIATION;
    [self miiWriteWord:mii_control reg:MII_CONTROL phy:phy];

    /*
     * If the system is not connected to the network, then auto-negotiation
     * never completes and we hang in this loop!
     */
#if 0
    while (1) 
    {
	[self miiReadWord:&mii_control reg:MII_CONTROL phy:phy];
	if ((mii_control & MII_CONTROL_RESTART_NEGOTIATION) == 0)
		break;
    }
#endif
}

/*
 * Find the first PHY device on the MII interface.
 *
 * Return
 *	YES		PHY found 
 *  	NO		PHY not found
 */
- (BOOL)miiFindPHY:(unsigned char *)phy
{
    int i;
	
    *phy = -1;

    // The first two PHY registers are required.
    //
    for (i = 0; i < MII_MAX_PHY; i++) 
    {
	if ([self miiReadWord:NULL reg:MII_STATUS phy:i] &&
		[self miiReadWord:NULL reg:MII_CONTROL phy:i])
		break;
    }
	
    if (i >= MII_MAX_PHY)
	return NO;

    *phy = i;

    return YES;
}

/*
 *
 *
 */
- (BOOL)miiInitializePHY:(unsigned char)phy
{
    u_int16_t		phyWord; 

    // Clear enable auto-negotiation bit
    //
    [self miiReadWord:&phyWord reg:MII_CONTROL phy:phy];
    phyWord &= ~MII_CONTROL_AUTONEGOTIATION;
    [self miiWriteWord:phyWord reg:MII_CONTROL phy:phy];

    // Advertise 10/100 Half/Full duplex capable to link partner
    //
    [self miiReadWord:&phyWord reg:MII_ADVERTISEMENT phy:phy];
    phyWord |= (MII_ANAR_100BASETX_FD | MII_ANAR_100BASETX |
                MII_ANAR_10BASET_FD   | MII_ANAR_10BASET );
    [self miiWriteWord:phyWord reg:MII_ADVERTISEMENT phy:phy];

    // Set enable auto-negotiation bit
    //
    [self miiReadWord:&phyWord reg:MII_CONTROL phy:phy];
    phyWord |= MII_CONTROL_AUTONEGOTIATION;
    [self miiWriteWord:phyWord reg:MII_CONTROL phy:phy];

    [self miiRestartAutoNegotiation:phy];

    return YES;
}        

@end