2019-01-21 19:05:50 +01:00
// SPDX-License-Identifier: GPL-2.0+
2017-03-21 16:36:37 +00:00
/*
* Core PHY library , taken from phy . c
*/
# include <linux/export.h>
# include <linux/phy.h>
2019-02-11 15:25:26 +01:00
# include <linux/of.h>
2017-03-21 16:36:37 +00:00
2017-07-25 15:02:42 +01:00
const char * phy_speed_to_str ( int speed )
{
2019-05-22 20:47:03 +02:00
BUILD_BUG_ON_MSG ( __ETHTOOL_LINK_MODE_MASK_NBITS ! = 69 ,
2019-04-13 20:53:43 +02:00
" Enum ethtool_link_mode_bit_indices and phylib are out of sync. "
" If a speed or mode has been added please update phy_speed_to_str "
" and the PHY settings array. \n " ) ;
2017-07-25 15:02:42 +01:00
switch ( speed ) {
case SPEED_10 :
return " 10Mbps " ;
case SPEED_100 :
return " 100Mbps " ;
case SPEED_1000 :
return " 1Gbps " ;
case SPEED_2500 :
return " 2.5Gbps " ;
case SPEED_5000 :
return " 5Gbps " ;
case SPEED_10000 :
return " 10Gbps " ;
case SPEED_14000 :
return " 14Gbps " ;
case SPEED_20000 :
return " 20Gbps " ;
case SPEED_25000 :
return " 25Gbps " ;
case SPEED_40000 :
return " 40Gbps " ;
case SPEED_50000 :
return " 50Gbps " ;
case SPEED_56000 :
return " 56Gbps " ;
case SPEED_100000 :
return " 100Gbps " ;
2019-04-13 20:50:24 +02:00
case SPEED_200000 :
return " 200Gbps " ;
2017-07-25 15:02:42 +01:00
case SPEED_UNKNOWN :
return " Unknown " ;
default :
return " Unsupported (update phy-core.c) " ;
}
}
EXPORT_SYMBOL_GPL ( phy_speed_to_str ) ;
const char * phy_duplex_to_str ( unsigned int duplex )
{
if ( duplex = = DUPLEX_HALF )
return " Half " ;
if ( duplex = = DUPLEX_FULL )
return " Full " ;
if ( duplex = = DUPLEX_UNKNOWN )
return " Unknown " ;
return " Unsupported (update phy-core.c) " ;
}
EXPORT_SYMBOL_GPL ( phy_duplex_to_str ) ;
2017-07-25 15:02:47 +01:00
/* A mapping of all SUPPORTED settings to speed/duplex. This table
* must be grouped by speed and sorted in descending match priority
* - iow , descending speed . */
2019-04-13 20:48:55 +02:00
# define PHY_SETTING(s, d, b) { .speed = SPEED_ ## s, .duplex = DUPLEX_ ## d, \
. bit = ETHTOOL_LINK_MODE_ # # b # # _BIT }
2017-07-25 15:02:47 +01:00
static const struct phy_setting settings [ ] = {
2019-04-13 20:50:24 +02:00
/* 200G */
PHY_SETTING ( 200000 , FULL , 200000 baseCR4_Full ) ,
PHY_SETTING ( 200000 , FULL , 200000 baseKR4_Full ) ,
PHY_SETTING ( 200000 , FULL , 200000 baseLR4_ER4_FR4_Full ) ,
PHY_SETTING ( 200000 , FULL , 200000 baseDR4_Full ) ,
PHY_SETTING ( 200000 , FULL , 200000 baseSR4_Full ) ,
2018-11-10 23:43:36 +01:00
/* 100G */
2019-04-13 20:48:55 +02:00
PHY_SETTING ( 100000 , FULL , 100000 baseCR4_Full ) ,
PHY_SETTING ( 100000 , FULL , 100000 baseKR4_Full ) ,
PHY_SETTING ( 100000 , FULL , 100000 baseLR4_ER4_Full ) ,
PHY_SETTING ( 100000 , FULL , 100000 baseSR4_Full ) ,
2019-04-13 20:50:24 +02:00
PHY_SETTING ( 100000 , FULL , 100000 baseCR2_Full ) ,
PHY_SETTING ( 100000 , FULL , 100000 baseKR2_Full ) ,
PHY_SETTING ( 100000 , FULL , 100000 baseLR2_ER2_FR2_Full ) ,
PHY_SETTING ( 100000 , FULL , 100000 baseDR2_Full ) ,
PHY_SETTING ( 100000 , FULL , 100000 baseSR2_Full ) ,
2018-11-10 23:43:36 +01:00
/* 56G */
2019-04-13 20:48:55 +02:00
PHY_SETTING ( 56000 , FULL , 56000 baseCR4_Full ) ,
PHY_SETTING ( 56000 , FULL , 56000 baseKR4_Full ) ,
PHY_SETTING ( 56000 , FULL , 56000 baseLR4_Full ) ,
PHY_SETTING ( 56000 , FULL , 56000 baseSR4_Full ) ,
2018-11-10 23:43:36 +01:00
/* 50G */
2019-04-13 20:48:55 +02:00
PHY_SETTING ( 50000 , FULL , 50000 baseCR2_Full ) ,
PHY_SETTING ( 50000 , FULL , 50000 baseKR2_Full ) ,
PHY_SETTING ( 50000 , FULL , 50000 baseSR2_Full ) ,
2019-04-13 20:50:24 +02:00
PHY_SETTING ( 50000 , FULL , 50000 baseCR_Full ) ,
PHY_SETTING ( 50000 , FULL , 50000 baseKR_Full ) ,
PHY_SETTING ( 50000 , FULL , 50000 baseLR_ER_FR_Full ) ,
PHY_SETTING ( 50000 , FULL , 50000 baseDR_Full ) ,
PHY_SETTING ( 50000 , FULL , 50000 baseSR_Full ) ,
2018-11-10 23:43:36 +01:00
/* 40G */
2019-04-13 20:48:55 +02:00
PHY_SETTING ( 40000 , FULL , 40000 baseCR4_Full ) ,
PHY_SETTING ( 40000 , FULL , 40000 baseKR4_Full ) ,
PHY_SETTING ( 40000 , FULL , 40000 baseLR4_Full ) ,
PHY_SETTING ( 40000 , FULL , 40000 baseSR4_Full ) ,
2018-11-10 23:43:36 +01:00
/* 25G */
2019-04-13 20:48:55 +02:00
PHY_SETTING ( 25000 , FULL , 25000 baseCR_Full ) ,
PHY_SETTING ( 25000 , FULL , 25000 baseKR_Full ) ,
PHY_SETTING ( 25000 , FULL , 25000 baseSR_Full ) ,
2018-11-10 23:43:36 +01:00
/* 20G */
2019-04-13 20:48:55 +02:00
PHY_SETTING ( 20000 , FULL , 20000 baseKR2_Full ) ,
PHY_SETTING ( 20000 , FULL , 20000 baseMLD2_Full ) ,
2018-11-10 23:43:36 +01:00
/* 10G */
2019-04-13 20:48:55 +02:00
PHY_SETTING ( 10000 , FULL , 10000 baseCR_Full ) ,
PHY_SETTING ( 10000 , FULL , 10000 baseER_Full ) ,
PHY_SETTING ( 10000 , FULL , 10000 baseKR_Full ) ,
PHY_SETTING ( 10000 , FULL , 10000 baseKX4_Full ) ,
PHY_SETTING ( 10000 , FULL , 10000 baseLR_Full ) ,
PHY_SETTING ( 10000 , FULL , 10000 baseLRM_Full ) ,
PHY_SETTING ( 10000 , FULL , 10000 baseR_FEC ) ,
PHY_SETTING ( 10000 , FULL , 10000 baseSR_Full ) ,
PHY_SETTING ( 10000 , FULL , 10000 baseT_Full ) ,
2018-11-10 23:43:36 +01:00
/* 5G */
2019-04-13 20:48:55 +02:00
PHY_SETTING ( 5000 , FULL , 5000 baseT_Full ) ,
2018-11-10 23:43:36 +01:00
/* 2.5G */
2019-04-13 20:48:55 +02:00
PHY_SETTING ( 2500 , FULL , 2500 baseT_Full ) ,
PHY_SETTING ( 2500 , FULL , 2500 baseX_Full ) ,
2018-11-10 23:43:36 +01:00
/* 1G */
2019-04-13 20:48:55 +02:00
PHY_SETTING ( 1000 , FULL , 1000 baseKX_Full ) ,
PHY_SETTING ( 1000 , FULL , 1000 baseT_Full ) ,
PHY_SETTING ( 1000 , HALF , 1000 baseT_Half ) ,
2019-05-22 20:47:03 +02:00
PHY_SETTING ( 1000 , FULL , 1000 baseT1_Full ) ,
2019-04-13 20:48:55 +02:00
PHY_SETTING ( 1000 , FULL , 1000 baseX_Full ) ,
2018-11-10 23:43:36 +01:00
/* 100M */
2019-04-13 20:48:55 +02:00
PHY_SETTING ( 100 , FULL , 100 baseT_Full ) ,
2019-05-22 20:47:03 +02:00
PHY_SETTING ( 100 , FULL , 100 baseT1_Full ) ,
2019-04-13 20:48:55 +02:00
PHY_SETTING ( 100 , HALF , 100 baseT_Half ) ,
2018-11-10 23:43:36 +01:00
/* 10M */
2019-04-13 20:48:55 +02:00
PHY_SETTING ( 10 , FULL , 10 baseT_Full ) ,
PHY_SETTING ( 10 , HALF , 10 baseT_Half ) ,
2017-07-25 15:02:47 +01:00
} ;
2019-04-13 20:48:55 +02:00
# undef PHY_SETTING
2017-07-25 15:02:47 +01:00
/**
* phy_lookup_setting - lookup a PHY setting
* @ speed : speed to match
* @ duplex : duplex to match
* @ mask : allowed link modes
* @ exact : an exact match is required
*
* Search the settings array for a setting that matches the speed and
* duplex , and which is supported .
*
* If @ exact is unset , either an exact match or % NULL for no match will
* be returned .
*
* If @ exact is set , an exact match , the fastest supported setting at
* or below the specified speed , the slowest supported setting , or if
* they all fail , % NULL will be returned .
*/
const struct phy_setting *
2018-11-10 23:43:33 +01:00
phy_lookup_setting ( int speed , int duplex , const unsigned long * mask , bool exact )
2017-07-25 15:02:47 +01:00
{
const struct phy_setting * p , * match = NULL , * last = NULL ;
int i ;
for ( i = 0 , p = settings ; i < ARRAY_SIZE ( settings ) ; i + + , p + + ) {
2018-11-10 23:43:33 +01:00
if ( p - > bit < __ETHTOOL_LINK_MODE_MASK_NBITS & &
test_bit ( p - > bit , mask ) ) {
2017-07-25 15:02:47 +01:00
last = p ;
if ( p - > speed = = speed & & p - > duplex = = duplex ) {
/* Exact match for speed and duplex */
match = p ;
break ;
} else if ( ! exact ) {
if ( ! match & & p - > speed < = speed )
/* Candidate */
match = p ;
if ( p - > speed < speed )
break ;
}
}
}
if ( ! match & & ! exact )
match = last ;
return match ;
}
EXPORT_SYMBOL_GPL ( phy_lookup_setting ) ;
size_t phy_speeds ( unsigned int * speeds , size_t size ,
2018-11-10 23:43:33 +01:00
unsigned long * mask )
2017-07-25 15:02:47 +01:00
{
size_t count ;
int i ;
for ( i = 0 , count = 0 ; i < ARRAY_SIZE ( settings ) & & count < size ; i + + )
2018-11-10 23:43:33 +01:00
if ( settings [ i ] . bit < __ETHTOOL_LINK_MODE_MASK_NBITS & &
2017-07-25 15:02:47 +01:00
test_bit ( settings [ i ] . bit , mask ) & &
( count = = 0 | | speeds [ count - 1 ] ! = settings [ i ] . speed ) )
speeds [ count + + ] = settings [ i ] . speed ;
return count ;
}
2019-08-12 23:50:30 +02:00
static int __set_linkmode_max_speed ( u32 max_speed , unsigned long * addr )
2019-02-11 15:25:26 +01:00
{
const struct phy_setting * p ;
int i ;
for ( i = 0 , p = settings ; i < ARRAY_SIZE ( settings ) ; i + + , p + + ) {
if ( p - > speed > max_speed )
2019-08-12 23:50:30 +02:00
linkmode_clear_bit ( p - > bit , addr ) ;
2019-02-11 15:25:26 +01:00
else
break ;
}
return 0 ;
}
2019-08-12 23:50:30 +02:00
static int __set_phy_supported ( struct phy_device * phydev , u32 max_speed )
{
return __set_linkmode_max_speed ( max_speed , phydev - > supported ) ;
}
2019-02-11 15:25:26 +01:00
int phy_set_max_speed ( struct phy_device * phydev , u32 max_speed )
{
int err ;
err = __set_phy_supported ( phydev , max_speed ) ;
if ( err )
return err ;
2019-05-01 21:34:43 +02:00
phy_advertise_supported ( phydev ) ;
2019-02-11 15:25:26 +01:00
return 0 ;
}
EXPORT_SYMBOL ( phy_set_max_speed ) ;
void of_set_phy_supported ( struct phy_device * phydev )
{
struct device_node * node = phydev - > mdio . dev . of_node ;
u32 max_speed ;
if ( ! IS_ENABLED ( CONFIG_OF_MDIO ) )
return ;
if ( ! node )
return ;
if ( ! of_property_read_u32 ( node , " max-speed " , & max_speed ) )
__set_phy_supported ( phydev , max_speed ) ;
}
2019-02-11 15:25:27 +01:00
void of_set_phy_eee_broken ( struct phy_device * phydev )
{
struct device_node * node = phydev - > mdio . dev . of_node ;
u32 broken = 0 ;
if ( ! IS_ENABLED ( CONFIG_OF_MDIO ) )
return ;
if ( ! node )
return ;
if ( of_property_read_bool ( node , " eee-broken-100tx " ) )
broken | = MDIO_EEE_100TX ;
if ( of_property_read_bool ( node , " eee-broken-1000t " ) )
broken | = MDIO_EEE_1000T ;
if ( of_property_read_bool ( node , " eee-broken-10gt " ) )
broken | = MDIO_EEE_10GT ;
if ( of_property_read_bool ( node , " eee-broken-1000kx " ) )
broken | = MDIO_EEE_1000KX ;
if ( of_property_read_bool ( node , " eee-broken-10gkx4 " ) )
broken | = MDIO_EEE_10GKX4 ;
if ( of_property_read_bool ( node , " eee-broken-10gkr " ) )
broken | = MDIO_EEE_10GKR ;
phydev - > eee_broken_modes = broken ;
}
2017-12-29 12:46:38 +00:00
/**
2018-03-01 10:23:03 +00:00
* phy_resolve_aneg_linkmode - resolve the advertisements into phy settings
2017-12-29 12:46:38 +00:00
* @ phydev : The phy_device struct
*
2018-03-01 10:23:03 +00:00
* Resolve our and the link partner advertisements into their corresponding
2017-12-29 12:46:38 +00:00
* speed and duplex . If full duplex was negotiated , extract the pause mode
* from the link partner mask .
*/
void phy_resolve_aneg_linkmode ( struct phy_device * phydev )
{
2018-11-10 23:43:33 +01:00
__ETHTOOL_DECLARE_LINK_MODE_MASK ( common ) ;
2019-02-14 22:15:31 +01:00
int i ;
2017-12-29 12:46:38 +00:00
2018-11-10 23:43:34 +01:00
linkmode_and ( common , phydev - > lp_advertising , phydev - > advertising ) ;
2018-11-10 23:43:33 +01:00
2019-02-14 22:15:31 +01:00
for ( i = 0 ; i < ARRAY_SIZE ( settings ) ; i + + )
if ( test_bit ( settings [ i ] . bit , common ) ) {
phydev - > speed = settings [ i ] . speed ;
phydev - > duplex = settings [ i ] . duplex ;
break ;
}
2017-12-29 12:46:38 +00:00
if ( phydev - > duplex = = DUPLEX_FULL ) {
2018-11-10 23:43:34 +01:00
phydev - > pause = linkmode_test_bit ( ETHTOOL_LINK_MODE_Pause_BIT ,
phydev - > lp_advertising ) ;
phydev - > asym_pause = linkmode_test_bit (
ETHTOOL_LINK_MODE_Asym_Pause_BIT ,
phydev - > lp_advertising ) ;
2017-12-29 12:46:38 +00:00
}
}
EXPORT_SYMBOL_GPL ( phy_resolve_aneg_linkmode ) ;
2019-08-12 23:51:27 +02:00
static int phy_resolve_min_speed ( struct phy_device * phydev , bool fdx_only )
{
__ETHTOOL_DECLARE_LINK_MODE_MASK ( common ) ;
int i = ARRAY_SIZE ( settings ) ;
linkmode_and ( common , phydev - > lp_advertising , phydev - > advertising ) ;
while ( - - i > = 0 ) {
if ( test_bit ( settings [ i ] . bit , common ) ) {
if ( fdx_only & & settings [ i ] . duplex ! = DUPLEX_FULL )
continue ;
return settings [ i ] . speed ;
}
}
return SPEED_UNKNOWN ;
}
int phy_speed_down_core ( struct phy_device * phydev )
{
int min_common_speed = phy_resolve_min_speed ( phydev , true ) ;
if ( min_common_speed = = SPEED_UNKNOWN )
return - EINVAL ;
return __set_linkmode_max_speed ( min_common_speed , phydev - > advertising ) ;
}
2017-03-21 16:37:08 +00:00
static void mmd_phy_indirect ( struct mii_bus * bus , int phy_addr , int devad ,
u16 regnum )
2017-03-21 16:36:37 +00:00
{
/* Write the desired MMD Devad */
2018-01-02 10:58:32 +00:00
__mdiobus_write ( bus , phy_addr , MII_MMD_CTRL , devad ) ;
2017-03-21 16:36:37 +00:00
/* Write the desired MMD register address */
2018-01-02 10:58:32 +00:00
__mdiobus_write ( bus , phy_addr , MII_MMD_DATA , regnum ) ;
2017-03-21 16:36:37 +00:00
/* Select the Function : DATA with no post increment */
2018-01-02 10:58:32 +00:00
__mdiobus_write ( bus , phy_addr , MII_MMD_CTRL ,
devad | MII_MMD_CTRL_NOINCR ) ;
2017-03-21 16:36:37 +00:00
}
/**
2019-02-06 07:36:40 +01:00
* __phy_read_mmd - Convenience function for reading a register
2017-03-21 16:36:37 +00:00
* from an MMD on a given PHY .
* @ phydev : The phy_device struct
2017-03-21 16:37:03 +00:00
* @ devad : The MMD to read from ( 0. .31 )
* @ regnum : The register on the MMD to read ( 0. .65535 )
2017-03-21 16:36:37 +00:00
*
2019-02-06 07:36:40 +01:00
* Same rules as for __phy_read ( ) ;
2017-03-21 16:36:37 +00:00
*/
2019-02-06 07:36:40 +01:00
int __phy_read_mmd ( struct phy_device * phydev , int devad , u32 regnum )
2017-03-21 16:36:37 +00:00
{
2017-03-21 16:37:03 +00:00
int val ;
2017-03-21 16:36:43 +00:00
if ( regnum > ( u16 ) ~ 0 | | devad > 32 )
return - EINVAL ;
2017-03-21 16:36:37 +00:00
2017-03-21 16:37:03 +00:00
if ( phydev - > drv - > read_mmd ) {
val = phydev - > drv - > read_mmd ( phydev , devad , regnum ) ;
} else if ( phydev - > is_c45 ) {
2017-03-21 16:36:43 +00:00
u32 addr = MII_ADDR_C45 | ( devad < < 16 ) | ( regnum & 0xffff ) ;
2019-02-06 07:36:40 +01:00
val = __mdiobus_read ( phydev - > mdio . bus , phydev - > mdio . addr , addr ) ;
2017-03-21 16:37:03 +00:00
} else {
2017-03-21 16:36:37 +00:00
struct mii_bus * bus = phydev - > mdio . bus ;
2017-03-21 16:37:03 +00:00
int phy_addr = phydev - > mdio . addr ;
2017-03-21 16:36:37 +00:00
2017-03-21 16:37:08 +00:00
mmd_phy_indirect ( bus , phy_addr , devad , regnum ) ;
2017-03-21 16:36:37 +00:00
2017-03-21 16:37:03 +00:00
/* Read the content of the MMD's selected register */
2018-01-02 10:58:32 +00:00
val = __mdiobus_read ( bus , phy_addr , MII_MMD_DATA ) ;
2017-03-21 16:36:37 +00:00
}
2017-03-21 16:37:03 +00:00
return val ;
2017-03-21 16:36:37 +00:00
}
2019-02-06 07:36:40 +01:00
EXPORT_SYMBOL ( __phy_read_mmd ) ;
/**
* phy_read_mmd - Convenience function for reading a register
* from an MMD on a given PHY .
* @ phydev : The phy_device struct
* @ devad : The MMD to read from
* @ regnum : The register on the MMD to read
*
* Same rules as for phy_read ( ) ;
*/
int phy_read_mmd ( struct phy_device * phydev , int devad , u32 regnum )
{
int ret ;
mutex_lock ( & phydev - > mdio . bus - > mdio_lock ) ;
ret = __phy_read_mmd ( phydev , devad , regnum ) ;
mutex_unlock ( & phydev - > mdio . bus - > mdio_lock ) ;
return ret ;
}
2017-03-21 16:37:03 +00:00
EXPORT_SYMBOL ( phy_read_mmd ) ;
2017-03-21 16:36:37 +00:00
/**
2019-02-06 07:36:40 +01:00
* __phy_write_mmd - Convenience function for writing a register
2017-03-21 16:36:37 +00:00
* on an MMD on a given PHY .
* @ phydev : The phy_device struct
* @ devad : The MMD to read from
* @ regnum : The register on the MMD to read
* @ val : value to write to @ regnum
*
2019-02-06 07:36:40 +01:00
* Same rules as for __phy_write ( ) ;
2017-03-21 16:36:37 +00:00
*/
2019-02-06 07:36:40 +01:00
int __phy_write_mmd ( struct phy_device * phydev , int devad , u32 regnum , u16 val )
2017-03-21 16:36:37 +00:00
{
2017-03-21 16:37:03 +00:00
int ret ;
2017-03-21 16:36:43 +00:00
if ( regnum > ( u16 ) ~ 0 | | devad > 32 )
return - EINVAL ;
2017-04-14 22:10:41 +03:00
if ( phydev - > drv - > write_mmd ) {
2017-03-21 16:37:03 +00:00
ret = phydev - > drv - > write_mmd ( phydev , devad , regnum , val ) ;
} else if ( phydev - > is_c45 ) {
2017-03-21 16:36:43 +00:00
u32 addr = MII_ADDR_C45 | ( devad < < 16 ) | ( regnum & 0xffff ) ;
2019-02-06 07:36:40 +01:00
ret = __mdiobus_write ( phydev - > mdio . bus , phydev - > mdio . addr ,
addr , val ) ;
2017-03-21 16:37:03 +00:00
} else {
struct mii_bus * bus = phydev - > mdio . bus ;
int phy_addr = phydev - > mdio . addr ;
2017-03-21 16:36:37 +00:00
2017-03-21 16:37:08 +00:00
mmd_phy_indirect ( bus , phy_addr , devad , regnum ) ;
2017-03-21 16:36:37 +00:00
2017-03-21 16:37:03 +00:00
/* Write the data into MMD's selected register */
2018-01-02 10:58:32 +00:00
__mdiobus_write ( bus , phy_addr , MII_MMD_DATA , val ) ;
2017-03-21 16:37:03 +00:00
ret = 0 ;
}
return ret ;
2017-03-21 16:36:37 +00:00
}
2019-02-06 07:36:40 +01:00
EXPORT_SYMBOL ( __phy_write_mmd ) ;
/**
* phy_write_mmd - Convenience function for writing a register
* on an MMD on a given PHY .
* @ phydev : The phy_device struct
* @ devad : The MMD to read from
* @ regnum : The register on the MMD to read
* @ val : value to write to @ regnum
*
* Same rules as for phy_write ( ) ;
*/
int phy_write_mmd ( struct phy_device * phydev , int devad , u32 regnum , u16 val )
{
int ret ;
mutex_lock ( & phydev - > mdio . bus - > mdio_lock ) ;
ret = __phy_write_mmd ( phydev , devad , regnum , val ) ;
mutex_unlock ( & phydev - > mdio . bus - > mdio_lock ) ;
return ret ;
}
2017-03-21 16:36:37 +00:00
EXPORT_SYMBOL ( phy_write_mmd ) ;
2018-01-02 10:58:37 +00:00
/**
2019-02-10 19:57:56 +01:00
* __phy_modify_changed ( ) - Convenience function for modifying a PHY register
2018-01-02 10:58:37 +00:00
* @ phydev : a pointer to a & struct phy_device
* @ regnum : register number
* @ mask : bit mask of bits to clear
* @ set : bit mask of bits to set
*
* Unlocked helper function which allows a PHY register to be modified as
2018-01-05 16:07:10 +00:00
* new register value = ( old register value & ~ mask ) | set
2019-02-10 19:57:56 +01:00
*
* Returns negative errno , 0 if there was no change , and 1 in case of change
2018-01-02 10:58:37 +00:00
*/
2019-02-10 19:57:56 +01:00
int __phy_modify_changed ( struct phy_device * phydev , u32 regnum , u16 mask ,
u16 set )
2018-01-02 10:58:37 +00:00
{
2019-02-10 19:57:56 +01:00
int new , ret ;
2018-01-02 10:58:37 +00:00
ret = __phy_read ( phydev , regnum ) ;
2018-01-12 15:01:36 +01:00
if ( ret < 0 )
return ret ;
2018-01-02 10:58:37 +00:00
2019-02-10 19:57:56 +01:00
new = ( ret & ~ mask ) | set ;
if ( new = = ret )
return 0 ;
ret = __phy_write ( phydev , regnum , new ) ;
return ret < 0 ? ret : 1 ;
}
EXPORT_SYMBOL_GPL ( __phy_modify_changed ) ;
/**
* phy_modify_changed - Function for modifying a PHY register
* @ phydev : the phy_device struct
* @ regnum : register number to modify
* @ mask : bit mask of bits to clear
* @ set : new value of bits set in mask to write to @ regnum
*
* NOTE : MUST NOT be called from interrupt context ,
* because the bus read / write functions may wait for an interrupt
* to conclude the operation .
*
* Returns negative errno , 0 if there was no change , and 1 in case of change
*/
int phy_modify_changed ( struct phy_device * phydev , u32 regnum , u16 mask , u16 set )
{
int ret ;
mutex_lock ( & phydev - > mdio . bus - > mdio_lock ) ;
ret = __phy_modify_changed ( phydev , regnum , mask , set ) ;
mutex_unlock ( & phydev - > mdio . bus - > mdio_lock ) ;
return ret ;
}
EXPORT_SYMBOL_GPL ( phy_modify_changed ) ;
/**
* __phy_modify - Convenience function for modifying a PHY register
* @ phydev : the phy_device struct
* @ regnum : register number to modify
* @ mask : bit mask of bits to clear
* @ set : new value of bits set in mask to write to @ regnum
*
* NOTE : MUST NOT be called from interrupt context ,
* because the bus read / write functions may wait for an interrupt
* to conclude the operation .
*/
int __phy_modify ( struct phy_device * phydev , u32 regnum , u16 mask , u16 set )
{
int ret ;
ret = __phy_modify_changed ( phydev , regnum , mask , set ) ;
2018-01-12 15:01:36 +01:00
return ret < 0 ? ret : 0 ;
2018-01-02 10:58:37 +00:00
}
EXPORT_SYMBOL_GPL ( __phy_modify ) ;
2018-01-02 10:58:43 +00:00
2018-01-02 10:58:53 +00:00
/**
* phy_modify - Convenience function for modifying a given PHY register
* @ phydev : the phy_device struct
* @ regnum : register number to write
* @ mask : bit mask of bits to clear
* @ set : new value of bits set in mask to write to @ regnum
*
* NOTE : MUST NOT be called from interrupt context ,
* because the bus read / write functions may wait for an interrupt
* to conclude the operation .
*/
int phy_modify ( struct phy_device * phydev , u32 regnum , u16 mask , u16 set )
{
int ret ;
mutex_lock ( & phydev - > mdio . bus - > mdio_lock ) ;
ret = __phy_modify ( phydev , regnum , mask , set ) ;
mutex_unlock ( & phydev - > mdio . bus - > mdio_lock ) ;
return ret ;
}
EXPORT_SYMBOL_GPL ( phy_modify ) ;
2019-02-06 07:36:40 +01:00
/**
2019-02-10 19:57:56 +01:00
* __phy_modify_mmd_changed - Function for modifying a register on MMD
2019-02-06 07:36:40 +01:00
* @ phydev : the phy_device struct
* @ devad : the MMD containing register to modify
* @ regnum : register number to modify
* @ mask : bit mask of bits to clear
* @ set : new value of bits set in mask to write to @ regnum
*
* Unlocked helper function which allows a MMD register to be modified as
* new register value = ( old register value & ~ mask ) | set
2019-02-10 19:57:56 +01:00
*
* Returns negative errno , 0 if there was no change , and 1 in case of change
2019-02-06 07:36:40 +01:00
*/
2019-02-10 19:57:56 +01:00
int __phy_modify_mmd_changed ( struct phy_device * phydev , int devad , u32 regnum ,
u16 mask , u16 set )
2019-02-06 07:36:40 +01:00
{
2019-02-10 19:57:56 +01:00
int new , ret ;
2019-02-06 07:36:40 +01:00
ret = __phy_read_mmd ( phydev , devad , regnum ) ;
if ( ret < 0 )
return ret ;
2019-02-10 19:57:56 +01:00
new = ( ret & ~ mask ) | set ;
if ( new = = ret )
return 0 ;
ret = __phy_write_mmd ( phydev , devad , regnum , new ) ;
return ret < 0 ? ret : 1 ;
}
EXPORT_SYMBOL_GPL ( __phy_modify_mmd_changed ) ;
/**
* phy_modify_mmd_changed - Function for modifying a register on MMD
* @ phydev : the phy_device struct
* @ devad : the MMD containing register to modify
* @ regnum : register number to modify
* @ mask : bit mask of bits to clear
* @ set : new value of bits set in mask to write to @ regnum
*
* NOTE : MUST NOT be called from interrupt context ,
* because the bus read / write functions may wait for an interrupt
* to conclude the operation .
*
* Returns negative errno , 0 if there was no change , and 1 in case of change
*/
int phy_modify_mmd_changed ( struct phy_device * phydev , int devad , u32 regnum ,
u16 mask , u16 set )
{
int ret ;
mutex_lock ( & phydev - > mdio . bus - > mdio_lock ) ;
ret = __phy_modify_mmd_changed ( phydev , devad , regnum , mask , set ) ;
mutex_unlock ( & phydev - > mdio . bus - > mdio_lock ) ;
return ret ;
}
EXPORT_SYMBOL_GPL ( phy_modify_mmd_changed ) ;
/**
* __phy_modify_mmd - Convenience function for modifying a register on MMD
* @ phydev : the phy_device struct
* @ devad : the MMD containing register to modify
* @ regnum : register number to modify
* @ mask : bit mask of bits to clear
* @ set : new value of bits set in mask to write to @ regnum
*
* NOTE : MUST NOT be called from interrupt context ,
* because the bus read / write functions may wait for an interrupt
* to conclude the operation .
*/
int __phy_modify_mmd ( struct phy_device * phydev , int devad , u32 regnum ,
u16 mask , u16 set )
{
int ret ;
ret = __phy_modify_mmd_changed ( phydev , devad , regnum , mask , set ) ;
2019-02-06 07:36:40 +01:00
return ret < 0 ? ret : 0 ;
}
EXPORT_SYMBOL_GPL ( __phy_modify_mmd ) ;
/**
* phy_modify_mmd - Convenience function for modifying a register on MMD
* @ phydev : the phy_device struct
* @ devad : the MMD containing register to modify
* @ regnum : register number to modify
* @ mask : bit mask of bits to clear
* @ set : new value of bits set in mask to write to @ regnum
*
* NOTE : MUST NOT be called from interrupt context ,
* because the bus read / write functions may wait for an interrupt
* to conclude the operation .
*/
int phy_modify_mmd ( struct phy_device * phydev , int devad , u32 regnum ,
u16 mask , u16 set )
{
int ret ;
mutex_lock ( & phydev - > mdio . bus - > mdio_lock ) ;
ret = __phy_modify_mmd ( phydev , devad , regnum , mask , set ) ;
mutex_unlock ( & phydev - > mdio . bus - > mdio_lock ) ;
return ret ;
}
EXPORT_SYMBOL_GPL ( phy_modify_mmd ) ;
2018-01-02 10:58:43 +00:00
static int __phy_read_page ( struct phy_device * phydev )
{
return phydev - > drv - > read_page ( phydev ) ;
}
static int __phy_write_page ( struct phy_device * phydev , int page )
{
return phydev - > drv - > write_page ( phydev , page ) ;
}
/**
* phy_save_page ( ) - take the bus lock and save the current page
* @ phydev : a pointer to a & struct phy_device
*
* Take the MDIO bus lock , and return the current page number . On error ,
* returns a negative errno . phy_restore_page ( ) must always be called
* after this , irrespective of success or failure of this call .
*/
int phy_save_page ( struct phy_device * phydev )
{
mutex_lock ( & phydev - > mdio . bus - > mdio_lock ) ;
return __phy_read_page ( phydev ) ;
}
EXPORT_SYMBOL_GPL ( phy_save_page ) ;
/**
* phy_select_page ( ) - take the bus lock , save the current page , and set a page
* @ phydev : a pointer to a & struct phy_device
* @ page : desired page
*
* Take the MDIO bus lock to protect against concurrent access , save the
* current PHY page , and set the current page . On error , returns a
* negative errno , otherwise returns the previous page number .
* phy_restore_page ( ) must always be called after this , irrespective
* of success or failure of this call .
*/
int phy_select_page ( struct phy_device * phydev , int page )
{
int ret , oldpage ;
oldpage = ret = phy_save_page ( phydev ) ;
if ( ret < 0 )
return ret ;
if ( oldpage ! = page ) {
ret = __phy_write_page ( phydev , page ) ;
if ( ret < 0 )
return ret ;
}
return oldpage ;
}
EXPORT_SYMBOL_GPL ( phy_select_page ) ;
/**
* phy_restore_page ( ) - restore the page register and release the bus lock
* @ phydev : a pointer to a & struct phy_device
* @ oldpage : the old page , return value from phy_save_page ( ) or phy_select_page ( )
* @ ret : operation ' s return code
*
* Release the MDIO bus lock , restoring @ oldpage if it is a valid page .
* This function propagates the earliest error code from the group of
* operations .
*
* Returns :
* @ oldpage if it was a negative value , otherwise
* @ ret if it was a negative errno value , otherwise
* phy_write_page ( ) ' s negative value if it were in error , otherwise
* @ ret .
*/
int phy_restore_page ( struct phy_device * phydev , int oldpage , int ret )
{
int r ;
if ( oldpage > = 0 ) {
r = __phy_write_page ( phydev , oldpage ) ;
/* Propagate the operation return code if the page write
* was successful .
*/
if ( ret > = 0 & & r < 0 )
ret = r ;
} else {
/* Propagate the phy page selection error code */
ret = oldpage ;
}
mutex_unlock ( & phydev - > mdio . bus - > mdio_lock ) ;
return ret ;
}
EXPORT_SYMBOL_GPL ( phy_restore_page ) ;
/**
* phy_read_paged ( ) - Convenience function for reading a paged register
* @ phydev : a pointer to a & struct phy_device
* @ page : the page for the phy
* @ regnum : register number
*
* Same rules as for phy_read ( ) .
*/
int phy_read_paged ( struct phy_device * phydev , int page , u32 regnum )
{
int ret = 0 , oldpage ;
oldpage = phy_select_page ( phydev , page ) ;
if ( oldpage > = 0 )
ret = __phy_read ( phydev , regnum ) ;
return phy_restore_page ( phydev , oldpage , ret ) ;
}
EXPORT_SYMBOL ( phy_read_paged ) ;
/**
* phy_write_paged ( ) - Convenience function for writing a paged register
* @ phydev : a pointer to a & struct phy_device
* @ page : the page for the phy
* @ regnum : register number
* @ val : value to write
*
* Same rules as for phy_write ( ) .
*/
int phy_write_paged ( struct phy_device * phydev , int page , u32 regnum , u16 val )
{
int ret = 0 , oldpage ;
oldpage = phy_select_page ( phydev , page ) ;
if ( oldpage > = 0 )
ret = __phy_write ( phydev , regnum , val ) ;
return phy_restore_page ( phydev , oldpage , ret ) ;
}
EXPORT_SYMBOL ( phy_write_paged ) ;
/**
2019-08-09 20:44:22 +02:00
* phy_modify_paged_changed ( ) - Function for modifying a paged register
2018-01-02 10:58:43 +00:00
* @ phydev : a pointer to a & struct phy_device
* @ page : the page for the phy
* @ regnum : register number
* @ mask : bit mask of bits to clear
* @ set : bit mask of bits to set
*
2019-08-09 20:44:22 +02:00
* Returns negative errno , 0 if there was no change , and 1 in case of change
2018-01-02 10:58:43 +00:00
*/
2019-08-09 20:44:22 +02:00
int phy_modify_paged_changed ( struct phy_device * phydev , int page , u32 regnum ,
u16 mask , u16 set )
2018-01-02 10:58:43 +00:00
{
int ret = 0 , oldpage ;
oldpage = phy_select_page ( phydev , page ) ;
if ( oldpage > = 0 )
2019-08-09 20:44:22 +02:00
ret = __phy_modify_changed ( phydev , regnum , mask , set ) ;
2018-01-02 10:58:43 +00:00
return phy_restore_page ( phydev , oldpage , ret ) ;
}
2019-08-09 20:44:22 +02:00
EXPORT_SYMBOL ( phy_modify_paged_changed ) ;
/**
* phy_modify_paged ( ) - Convenience function for modifying a paged register
* @ phydev : a pointer to a & struct phy_device
* @ page : the page for the phy
* @ regnum : register number
* @ mask : bit mask of bits to clear
* @ set : bit mask of bits to set
*
* Same rules as for phy_read ( ) and phy_write ( ) .
*/
int phy_modify_paged ( struct phy_device * phydev , int page , u32 regnum ,
u16 mask , u16 set )
{
int ret = phy_modify_paged_changed ( phydev , page , regnum , mask , set ) ;
return ret < 0 ? ret : 0 ;
}
2018-01-02 10:58:43 +00:00
EXPORT_SYMBOL ( phy_modify_paged ) ;