/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/*
*
* Utility module to provide a consistent interface to a MAC driver accross
* different implementations of PHY devices
*/
#include <sys/sysmacros.h>
#include "dnet_mii.h"
#ifdef DEBUG
#define MIIDEBUG
int miidebug = 0;
#endif
/* Local functions */
/* Vendor specific callback function prototypes */
static void dump_NS83840(mii_handle_t, int);
static void dump_ICS1890(struct mii_info *, int);
static int getspeed_NS83840(mii_handle_t, int, int *, int *);
static int getspeed_82553(mii_handle_t, int, int *, int *);
static int getspeed_ICS1890(mii_handle_t, int, int *, int *);
static int getspeed_generic(mii_handle_t, int, int *, int *);
/*
* MII Interface functions
*/
/*
* Register an instance of an MII interface user
*/
int
{
/* Allocate space for the mii structure */
if ((mac = (mii_handle_t)
return (MII_NOMEM);
return (MII_SUCCESS);
}
/*
* Returns true if PHY at address phy is accessible. This should be
* considered the only function that takes a PHY address that can be called
* before mii_init_phy. There should be at least one bit set in the status
* register, and at least one clear
*/
int
{
return (MII_PARAM);
/* Clear any latched bits by reading twice */
#ifdef MIIDEBUG
#endif
/*
* At least one bit in status should be clear (one of the error
* bits), and there must be at least one bit set for the device
* capabilities. Unconnected devices tend to show 0xffff, but 0x0000
* has been seen.
*/
return (MII_PHYNOTPRESENT);
return (MII_SUCCESS);
}
/*
* Initialise PHY, and store info about it in the handle for future
* reference when the MAC calls us. PHY Vendor-specific code here isolates
* the LAN driver from worrying about different PHY implementations
*/
int
{
void *dip;
return (MII_PARAM);
/* Create a phydata structure for this new phy */
return (MII_PHYPRESENT);
if (!phydata)
return (MII_NOMEM);
/* Override speed and duplex mode from conf-file if present */
/*
* when explicitly setting speed or duplex, we must
* disable autonegotiation
*/
if (!(status & MII_STATUS_CANAUTONEG) ||
/*
* If local side cannot autonegotiate, we can't try to enable
* full duplex without the user's consent, because we cannot
* tell without AN if the partner can support it
*/
/* A very stupid PHY would not be supported */
return (MII_NOTSUPPORTED);
}
/* mii_sync will sort out the speed selection on the PHY */
} else
case NS_DP83840:
"National Semiconductor DP-83840";
break;
default:
break;
}
break;
case OUI_INTEL:
case INTEL_82553_CSTEP:
break;
case INTEL_82555:
break;
case INTEL_82562_EH:
break;
case INTEL_82562_ET:
break;
case INTEL_82562_EM:
break;
default:
break;
}
break;
case OUI_ICS:
case ICS_1890:
case ICS_1889:
break;
default:
break;
}
break;
default: /* Non-standard PHYs, that encode weird IDs */
break;
}
/* Do all post-reset hacks and user settings */
"dump-phy", 0))
return (MII_SUCCESS);
}
/*
* Cause a reset on a PHY
*/
int
{
int i;
return (MII_PARAM);
/* Strobe the reset bit in the control register */
/*
* This is likely to be very fast (ie, by the time we read the
* control register once, the devices we have seen can have already
* reset), but according to 802.3u 22.2.4.1.1, it could be up to .5 sec.
*/
for (i = 100; i--; ) {
if (!(control & MII_CONTROL_RESET))
break;
drv_usecwait(10);
}
if (i)
goto reset_completed;
}
if (wait == mii_wait_user) {
for (i = 50; i--; ) {
if (!(control & MII_CONTROL_RESET))
break;
}
if (i)
goto reset_completed;
return (MII_HARDFAIL); /* It MUST reset within this time */
}
return (MII_TIMEOUT);
return (MII_SUCCESS);
}
/*
* This routine is called to synchronise the software and the PHY. It should
* be called after the PHY is reset, and after initialising the PHY. This
* routine is external because devices (DNET) can reset the PHY in ways beyond
* the control of the mii interface. Should this happen, the driver is
* required to call mii_sync().
* If the PHY is resetting still when this is called, it will do nothing,
* but, it will be retriggered when the portmon timer expires.
*/
int
{
struct regprop {
int reg;
int value;
} *regprop;
#ifdef MIIDEBUG
#endif
len = 0;
/*
* Conf file can specify a sequence of values to write to
* the PHY registers if required
*/
&len) == DDI_PROP_SUCCESS) {
for (i = 0; i < numprop; i++) {
#ifdef MIIDEBUG
#endif
}
} else {
if (phyd->phy_postreset)
/* XXX function return value ignored */
phyd->fix_duplex);
}
}
return (MII_SUCCESS);
}
/*
* Disable full-duplex negotiation on the PHY. This is useful if the
* driver or link-partner is advertising full duplex, but does not support
* it properly (as some previous solaris drivers didn't)
*/
int
{
/* dont advertise full duplex capabilites */
/*
* Local side cannot autonegotiate, so full duplex should
* never be negotiated. Consider it as a success
*/
return (MII_SUCCESS);
}
/* Change what we advertise if it includes full duplex */
if (miiadvert & fullduplex)
miiadvert & ~fullduplex);
/* See what other end is able to do. */
/*
* Renegotiate if the link partner supports autonegotiation
* If it doesn't, we will never have auto-negotiated full duplex
* anyway
*/
if (expansion & MII_AN_EXP_LPCANAN)
else
return (MII_SUCCESS);
}
/*
* (re)enable autonegotiation on a PHY.
*/
int
{
return (MII_PARAM);
return (MII_SUCCESS);
}
/*
* Check the link status of a PHY connection
*/
int
{
/*
* Link status latches, so we need to read it twice, to make sure we
* get its current status
*/
return (1);
else
return (0);
}
/*
* Discover what speed the PHY is running at, irrespective of wheather it
* autonegotiated this, or was fixed at that rate.
*/
int
{
return (MII_PARAM);
/*
* user has requested fixed speed operation, return what we
* wrote to the control registerfrom control register
*/
return (MII_SUCCESS);
}
return (MII_NOTSUPPORTED);
}
/*
* Fix the speed and duplex mode of a PHY
*/
int
{
#ifdef MIIDEBUG
#endif
return (MII_PARAM);
if (speed == 100)
else if (speed == 10)
else
if (fullduplex)
else
return (MII_SUCCESS);
}
/*
*/
int
{
return (MII_PARAM);
/* Wait for device to settle */
drv_usecwait(50);
return (MII_SUCCESS);
}
int
{
return (MII_PARAM);
return (MII_SUCCESS);
}
/*
* Restart autonegotiation on a PHY
*/
int
{
int i;
void *dip;
if (wait == mii_wait_interrupt ||
return (MII_PARAM);
return (MII_STATE);
/*
* This can take ages (a second or so). It makes more sense to use
* the port monitor rather than waiting for completion of this on the
* PHY. It is pointless doing a busy wait here
*/
if (wait == mii_wait_user) {
for (i = 200; i--; ) {
return (MII_SUCCESS);
}
"!%s:Timed out waiting for autonegotiation",
return (MII_TIMEOUT);
}
return (MII_TIMEOUT);
}
/*
* Debuging function to dump contents of PHY registers
*/
int
{
char *miiregs[] = {
"Control ",
"Status ",
"PHY Id(H) ",
"PHY Id(L) ",
"Advertisement ",
"Link Partner Ability",
"Expansion ",
"Next Page Transmit ",
0
};
int i;
return (MII_PARAM);
for (i = 0; miiregs[i]; i++)
return (MII_SUCCESS);
}
/*
* Start a periodic check to monitor the MII devices attached, and callback
* to the MAC driver when the state on a device changes
*/
int
{
return (MII_STATE);
/*
* NOTE: Portmon is normally called through a timeout. In the case
* of starting off, we assume that the lock is already held
*/
return (MII_SUCCESS);
}
int
{
return (MII_STATE);
mac->portmon_timer = 0;
return (MII_SUCCESS);
}
static void
{
int i;
/*
* There is a potential deadlock between this test and the
* mutex_enter
*/
return;
/*
* For each initialised phy, see if the link state has changed, and
* callback to the mac driver if it has
*/
for (i = 0; i < 32; i++) {
#ifdef MIIDEBUG
if (miidebug)
state == phy_state_linkup ?
"up" : "down");
#endif
}
}
}
/* Check the ports every 5 seconds */
}
/*
* Close a handle to the MII interface from a registered user
*/
void
{
/* Free per-PHY information */
int i;
(void) mii_stop_portmon(mac);
for (i = 0; i < 32; i++)
}
/*
* Get a PHY data structure from an MII handle, and validate the common
* parameters to the MII functions. Used to verify parameters in most MII
* functions
*/
static struct phydata *
{
ASSERT(!"MII: Bad invocation");
return (NULL);
}
}
/*
* Device-specific routines - National Semiconductor
*/
static void
{
void *dip;
}
static int
{
if (exten & MII_AN_EXP_LPCANAN) {
/*
* Link partner can auto-neg, take speed from LP Ability
* register
*/
if (mask & MII_ABILITY_100BASE_TX_FD) {
*speed = 100;
*fulld = 1;
} else if (mask & MII_ABILITY_100BASE_T4) {
*speed = 100;
*fulld = 0;
} else if (mask & MII_ABILITY_100BASE_TX) {
*speed = 100;
*fulld = 0;
} else if (mask & MII_ABILITY_10BASE_T_FD) {
*speed = 10;
*fulld = 1;
} else if (mask & MII_ABILITY_10BASE_T) {
*speed = 10;
*fulld = 0;
}
} else {
/* No fullduplex without autonegotiation on link partner */
*fulld = 0;
}
return (0);
}
/*
* Device-specific routines - INTEL
*/
static int
{
return (0);
}
/*
* Device-specific routines - ICS
*/
static int
{
return (0);
}
static void
{
}
static void
{
/*
* As per INTEL "PRO/100B Adapter Software Technical
* Reference Manual", set bit 10 of MII register 23.
* National Semiconductor documentation shows this as
* "reserved, write to as zero". We also set the
* "f_connect" bit, also as requested by the PRO/100B
* doc
*/
/*
* Some of thses PHYs seem to reset with the wrong value in the
* AN advertisment register. It should containt 1e1, indicating that
* the device can do 802.3 10BASE-T, 10BASE-T Full duplex, 100BASE-TX,
* and 100 BASE-TX full duplex. Instead it seems to advertise only
* 100BASE-TX Full duplex. The result of this is that the device will
* autonegotiating hub
* NEEDSWORK:
* There is possibly a time-dependancy here.
* If the autonegotiation has completed BEFORE we get to here
* (after the reset) then this could possibly have not effect
*/
#ifdef MIIDEBUG
#endif
}
}
void
{
/* This device comes up isolated if no link is found */
}
/*
* generic getspeed routine
*/
static int
{
if (exten & MII_AN_EXP_LPCANAN) {
/*
* Link partner can auto-neg, take speed from LP Ability
* register
*/
if (mask & MII_ABILITY_100BASE_TX_FD) {
*speed = 100;
*fulld = 1;
} else if (mask & MII_ABILITY_100BASE_T4) {
*speed = 100;
*fulld = 0;
} else if (mask & MII_ABILITY_100BASE_TX) {
*speed = 100;
*fulld = 0;
} else if (mask & MII_ABILITY_10BASE_T_FD) {
*speed = 10;
*fulld = 1;
} else if (mask & MII_ABILITY_10BASE_T) {
*speed = 10;
*fulld = 0;
}
} else {
/*
* Link partner cannot auto-neg, it would be nice if we
* could figure out what the device selected. (NWay?)
*/
*speed = 0;
*fulld = 0;
}
return (MII_SUCCESS);
}