2016-06-23 14:50:05 +01:00
/*
* Software PHY emulation
*
* Code taken from fixed_phy . c by Russell King < rmk + kernel @ arm . linux . org . uk >
*
* Author : Vitaly Bordug < vbordug @ ru . mvista . com >
* Anton Vorontsov < avorontsov @ ru . mvista . com >
*
* Copyright ( c ) 2006 - 2007 MontaVista Software , Inc .
*
* This program is free software ; you can redistribute it and / or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation ; either version 2 of the License , or ( at your
* option ) any later version .
*/
# include <linux/export.h>
# include <linux/mii.h>
# include <linux/phy.h>
# include <linux/phy_fixed.h>
# include "swphy.h"
2016-06-23 14:50:20 +01:00
# define MII_REGS_NUM 29
2016-06-23 14:50:10 +01:00
struct swmii_regs {
u16 bmcr ;
u16 bmsr ;
u16 lpa ;
u16 lpagb ;
} ;
enum {
SWMII_SPEED_10 = 0 ,
SWMII_SPEED_100 ,
SWMII_SPEED_1000 ,
SWMII_DUPLEX_HALF = 0 ,
SWMII_DUPLEX_FULL ,
} ;
/*
* These two tables get bitwise - anded together to produce the final result .
* This means the speed table must contain both duplex settings , and the
* duplex table must contain all speed settings .
*/
static const struct swmii_regs speed [ ] = {
[ SWMII_SPEED_10 ] = {
. bmcr = BMCR_FULLDPLX ,
. lpa = LPA_10FULL | LPA_10HALF ,
} ,
[ SWMII_SPEED_100 ] = {
. bmcr = BMCR_FULLDPLX | BMCR_SPEED100 ,
. bmsr = BMSR_100FULL | BMSR_100HALF ,
. lpa = LPA_100FULL | LPA_100HALF ,
} ,
[ SWMII_SPEED_1000 ] = {
. bmcr = BMCR_FULLDPLX | BMCR_SPEED1000 ,
. bmsr = BMSR_ESTATEN ,
. lpagb = LPA_1000FULL | LPA_1000HALF ,
} ,
} ;
static const struct swmii_regs duplex [ ] = {
[ SWMII_DUPLEX_HALF ] = {
. bmcr = ~ BMCR_FULLDPLX ,
. bmsr = BMSR_ESTATEN | BMSR_100HALF ,
. lpa = LPA_10HALF | LPA_100HALF ,
. lpagb = LPA_1000HALF ,
} ,
[ SWMII_DUPLEX_FULL ] = {
. bmcr = ~ 0 ,
. bmsr = BMSR_ESTATEN | BMSR_100FULL ,
. lpa = LPA_10FULL | LPA_100FULL ,
. lpagb = LPA_1000FULL ,
} ,
} ;
static int swphy_decode_speed ( int speed )
{
switch ( speed ) {
case 1000 :
return SWMII_SPEED_1000 ;
case 100 :
return SWMII_SPEED_100 ;
case 10 :
return SWMII_SPEED_10 ;
default :
return - EINVAL ;
}
}
2016-06-23 14:50:15 +01:00
/**
* swphy_validate_state - validate the software phy status
* @ state : software phy status
*
* This checks that we can represent the state stored in @ state can be
* represented in the emulated MII registers . Returns 0 if it can ,
* otherwise returns - EINVAL .
*/
int swphy_validate_state ( const struct fixed_phy_status * state )
{
int err ;
if ( state - > link ) {
err = swphy_decode_speed ( state - > speed ) ;
if ( err < 0 ) {
pr_warn ( " swphy: unknown speed \n " ) ;
return - EINVAL ;
}
}
return 0 ;
}
EXPORT_SYMBOL_GPL ( swphy_validate_state ) ;
2016-06-23 14:50:05 +01:00
/**
2016-06-23 14:50:20 +01:00
* swphy_read_reg - return a MII register from the fixed phy state
* @ reg : MII register
2016-06-23 14:50:05 +01:00
* @ state : fixed phy status
*
2016-06-23 14:50:20 +01:00
* Return the MII @ reg register generated from the fixed phy state @ state .
2016-06-23 14:50:05 +01:00
*/
2016-06-23 14:50:20 +01:00
int swphy_read_reg ( int reg , const struct fixed_phy_status * state )
2016-06-23 14:50:05 +01:00
{
2016-06-23 14:50:10 +01:00
int speed_index , duplex_index ;
2016-06-23 14:50:05 +01:00
u16 bmsr = BMSR_ANEGCAPABLE ;
u16 bmcr = 0 ;
u16 lpagb = 0 ;
u16 lpa = 0 ;
2016-06-23 14:50:20 +01:00
if ( reg > MII_REGS_NUM )
return - 1 ;
2016-06-23 14:50:10 +01:00
speed_index = swphy_decode_speed ( state - > speed ) ;
2016-06-23 14:50:15 +01:00
if ( WARN_ON ( speed_index < 0 ) )
2016-06-23 14:50:20 +01:00
return 0 ;
2016-06-23 14:50:05 +01:00
2016-06-23 14:50:10 +01:00
duplex_index = state - > duplex ? SWMII_DUPLEX_FULL : SWMII_DUPLEX_HALF ;
bmsr | = speed [ speed_index ] . bmsr & duplex [ duplex_index ] . bmsr ;
2016-06-23 14:50:05 +01:00
if ( state - > link ) {
bmsr | = BMSR_LSTATUS | BMSR_ANEGCOMPLETE ;
2016-06-23 14:50:10 +01:00
bmcr | = speed [ speed_index ] . bmcr & duplex [ duplex_index ] . bmcr ;
lpa | = speed [ speed_index ] . lpa & duplex [ duplex_index ] . lpa ;
lpagb | = speed [ speed_index ] . lpagb & duplex [ duplex_index ] . lpagb ;
2016-06-23 14:50:05 +01:00
if ( state - > pause )
lpa | = LPA_PAUSE_CAP ;
if ( state - > asym_pause )
lpa | = LPA_PAUSE_ASYM ;
}
2016-06-23 14:50:20 +01:00
switch ( reg ) {
case MII_BMCR :
return bmcr ;
case MII_BMSR :
return bmsr ;
case MII_PHYSID1 :
case MII_PHYSID2 :
return 0 ;
case MII_LPA :
return lpa ;
case MII_STAT1000 :
return lpagb ;
/*
* We do not support emulating Clause 45 over Clause 22 register
* reads . Return an error instead of bogus data .
*/
case MII_MMD_CTRL :
case MII_MMD_DATA :
return - 1 ;
2016-06-23 14:50:05 +01:00
2016-06-23 14:50:20 +01:00
default :
return 0xffff ;
}
2016-06-23 14:50:05 +01:00
}
2016-06-23 14:50:20 +01:00
EXPORT_SYMBOL_GPL ( swphy_read_reg ) ;