2019-01-21 19:05:50 +01:00
// SPDX-License-Identifier: GPL-2.0+
2006-05-07 23:22:53 +02:00
/*
* drivers / net / phy / smsc . c
*
* Driver for SMSC PHYs
*
* Author : Herbert Valerio Riedel
*
* Copyright ( c ) 2006 Herbert Valerio Riedel < hvr @ gnu . org >
*
2012-04-16 12:13:29 +01:00
* Support added for SMSC LAN8187 and LAN8700 by steve . glendinning @ shawell . net
2008-04-28 18:37:29 +01:00
*
2006-05-07 23:22:53 +02:00
*/
2020-09-09 15:45:00 +02:00
# include <linux/clk.h>
2006-05-07 23:22:53 +02:00
# include <linux/kernel.h>
# include <linux/module.h>
# include <linux/mii.h>
# include <linux/ethtool.h>
2017-03-28 23:45:06 +02:00
# include <linux/of.h>
2006-05-07 23:22:53 +02:00
# include <linux/phy.h>
# include <linux/netdevice.h>
2023-07-25 16:54:30 -07:00
# include <linux/crc16.h>
# include <linux/etherdevice.h>
2012-01-03 20:23:18 -05:00
# include <linux/smscphy.h>
2006-05-07 23:22:53 +02:00
2020-08-26 13:17:17 +02:00
/* Vendor-specific PHY Definitions */
/* EDPD NLP / crossover time configuration */
# define PHY_EDPD_CONFIG 16
# define PHY_EDPD_CONFIG_EXT_CROSSOVER_ 0x0001
/* Control/Status Indication Register */
# define SPECIAL_CTRL_STS 27
# define SPECIAL_CTRL_STS_OVRRD_AMDIX_ 0x8000
# define SPECIAL_CTRL_STS_AMDIX_ENABLE_ 0x4000
# define SPECIAL_CTRL_STS_AMDIX_STATE_ 0x2000
2023-04-02 17:14:35 +02:00
# define EDPD_MAX_WAIT_DFLT_MS 640
2023-04-02 17:16:56 +02:00
/* interval between phylib state machine runs in ms */
# define PHY_STATE_MACH_MS 1000
2023-04-02 17:14:35 +02:00
2017-06-03 20:00:36 +02:00
struct smsc_hw_stat {
const char * string ;
u8 reg ;
u8 bits ;
} ;
static struct smsc_hw_stat smsc_hw_stats [ ] = {
{ " phy_symbol_errors " , 26 , 16 } ,
} ;
2016-01-20 13:40:35 +01:00
struct smsc_phy_priv {
2023-04-02 17:11:40 +02:00
unsigned int edpd_enable : 1 ;
2023-04-02 17:13:35 +02:00
unsigned int edpd_mode_set_by_user : 1 ;
2023-04-02 17:14:35 +02:00
unsigned int edpd_max_wait_ms ;
2023-07-25 16:54:30 -07:00
bool wol_arp ;
2016-01-20 13:40:35 +01:00
} ;
2020-11-13 18:52:22 +02:00
static int smsc_phy_ack_interrupt ( struct phy_device * phydev )
{
int rc = phy_read ( phydev , MII_LAN83C185_ISF ) ;
return rc < 0 ? rc : 0 ;
}
2023-03-18 21:32:41 +01:00
int smsc_phy_config_intr ( struct phy_device * phydev )
2006-05-07 23:22:53 +02:00
{
2020-09-09 15:44:57 +02:00
int rc ;
if ( phydev - > interrupts = = PHY_INTERRUPT_ENABLED ) {
2020-11-13 18:52:22 +02:00
rc = smsc_phy_ack_interrupt ( phydev ) ;
if ( rc )
return rc ;
2023-03-04 11:52:44 +01:00
rc = phy_write ( phydev , MII_LAN83C185_IM ,
MII_LAN83C185_ISF_INT_PHYLIB_EVENTS ) ;
2020-11-13 18:52:22 +02:00
} else {
2022-05-12 10:42:06 +02:00
rc = phy_write ( phydev , MII_LAN83C185_IM , 0 ) ;
2020-11-13 18:52:22 +02:00
if ( rc )
return rc ;
2006-05-07 23:22:53 +02:00
2020-11-13 18:52:22 +02:00
rc = smsc_phy_ack_interrupt ( phydev ) ;
}
2006-05-07 23:22:53 +02:00
return rc < 0 ? rc : 0 ;
}
2023-03-18 21:32:41 +01:00
EXPORT_SYMBOL_GPL ( smsc_phy_config_intr ) ;
2006-05-07 23:22:53 +02:00
2023-04-02 17:12:20 +02:00
static int smsc_phy_config_edpd ( struct phy_device * phydev )
{
struct smsc_phy_priv * priv = phydev - > priv ;
if ( priv - > edpd_enable )
return phy_set_bits ( phydev , MII_LAN83C185_CTRL_STATUS ,
MII_LAN83C185_EDPWRDOWN ) ;
else
return phy_clear_bits ( phydev , MII_LAN83C185_CTRL_STATUS ,
MII_LAN83C185_EDPWRDOWN ) ;
}
2023-03-18 21:32:41 +01:00
irqreturn_t smsc_phy_handle_interrupt ( struct phy_device * phydev )
2020-11-13 18:52:21 +02:00
{
2022-05-12 10:42:06 +02:00
int irq_status ;
2020-11-13 18:52:21 +02:00
irq_status = phy_read ( phydev , MII_LAN83C185_ISF ) ;
if ( irq_status < 0 ) {
2022-05-12 10:42:07 +02:00
if ( irq_status ! = - ENODEV )
phy_error ( phydev ) ;
2020-11-13 18:52:21 +02:00
return IRQ_NONE ;
}
2023-03-04 11:52:44 +01:00
if ( ! ( irq_status & MII_LAN83C185_ISF_INT_PHYLIB_EVENTS ) )
2020-11-13 18:52:21 +02:00
return IRQ_NONE ;
phy_trigger_machine ( phydev ) ;
return IRQ_HANDLED ;
}
2023-03-18 21:32:41 +01:00
EXPORT_SYMBOL_GPL ( smsc_phy_handle_interrupt ) ;
2020-11-13 18:52:21 +02:00
2023-03-18 21:32:41 +01:00
int smsc_phy_config_init ( struct phy_device * phydev )
2014-08-15 15:00:38 +02:00
{
2016-01-20 13:40:35 +01:00
struct smsc_phy_priv * priv = phydev - > priv ;
2020-09-09 15:44:58 +02:00
2023-04-02 17:13:05 +02:00
if ( ! priv )
2020-09-09 15:44:58 +02:00
return 0 ;
2016-01-20 13:40:35 +01:00
2023-04-02 17:13:35 +02:00
/* don't use EDPD in irq mode except overridden by user */
if ( ! priv - > edpd_mode_set_by_user & & phydev - > irq ! = PHY_POLL )
2023-04-02 17:13:05 +02:00
priv - > edpd_enable = false ;
2023-04-02 17:12:20 +02:00
return smsc_phy_config_edpd ( phydev ) ;
2014-08-15 15:00:38 +02:00
}
2023-03-18 21:32:41 +01:00
EXPORT_SYMBOL_GPL ( smsc_phy_config_init ) ;
2014-08-15 15:00:38 +02:00
static int smsc_phy_reset ( struct phy_device * phydev )
2006-05-07 23:22:53 +02:00
{
2012-12-04 08:52:10 +00:00
int rc = phy_read ( phydev , MII_LAN83C185_SPECIAL_MODES ) ;
if ( rc < 0 )
return rc ;
/* If the SMSC PHY is in power down mode, then set it
* in all capable mode before using it .
*/
if ( ( rc & MII_LAN83C185_MODE_MASK ) = = MII_LAN83C185_MODE_POWERDOWN ) {
2016-06-06 10:47:47 +02:00
/* set "all capable" mode */
2012-12-04 08:52:10 +00:00
rc | = MII_LAN83C185_MODE_ALL ;
phy_write ( phydev , MII_LAN83C185_SPECIAL_MODES , rc ) ;
}
2016-06-06 10:47:47 +02:00
/* reset the phy */
return genphy_soft_reset ( phydev ) ;
2006-05-07 23:22:53 +02:00
}
2020-08-26 13:17:17 +02:00
static int lan87xx_config_aneg ( struct phy_device * phydev )
{
int rc ;
int val ;
switch ( phydev - > mdix_ctrl ) {
case ETH_TP_MDI :
val = SPECIAL_CTRL_STS_OVRRD_AMDIX_ ;
break ;
case ETH_TP_MDI_X :
val = SPECIAL_CTRL_STS_OVRRD_AMDIX_ |
SPECIAL_CTRL_STS_AMDIX_STATE_ ;
break ;
case ETH_TP_MDI_AUTO :
val = SPECIAL_CTRL_STS_AMDIX_ENABLE_ ;
break ;
default :
return genphy_config_aneg ( phydev ) ;
}
rc = phy_read ( phydev , SPECIAL_CTRL_STS ) ;
if ( rc < 0 )
return rc ;
rc & = ~ ( SPECIAL_CTRL_STS_OVRRD_AMDIX_ |
SPECIAL_CTRL_STS_AMDIX_ENABLE_ |
SPECIAL_CTRL_STS_AMDIX_STATE_ ) ;
rc | = val ;
phy_write ( phydev , SPECIAL_CTRL_STS , rc ) ;
phydev - > mdix = phydev - > mdix_ctrl ;
return genphy_config_aneg ( phydev ) ;
}
2021-03-29 11:45:36 +02:00
static int lan95xx_config_aneg_ext ( struct phy_device * phydev )
2020-08-26 13:17:17 +02:00
{
2023-03-06 23:10:57 +01:00
if ( phydev - > phy_id = = 0x0007c0f0 ) { /* LAN9500A or LAN9505A */
/* Extend Manual AutoMDIX timer */
int rc = phy_set_bits ( phydev , PHY_EDPD_CONFIG ,
PHY_EDPD_CONFIG_EXT_CROSSOVER_ ) ;
2021-03-29 11:45:36 +02:00
2023-03-06 23:10:57 +01:00
if ( rc < 0 )
return rc ;
}
2020-08-26 13:17:17 +02:00
return lan87xx_config_aneg ( phydev ) ;
}
2012-11-15 09:00:57 +00:00
/*
2015-08-14 20:11:02 +03:00
* The LAN87xx suffers from rare absence of the ENERGYON - bit when Ethernet cable
* plugs in while LAN87xx is in Energy Detect Power - Down mode . This leads to
* unstable detection of plugging in Ethernet cable .
* This workaround disables Energy Detect Power - Down mode and waiting for
* response on link pulses to detect presence of plugged Ethernet cable .
* The Energy Detect Power - Down mode is enabled again in the end of procedure to
* save approximately 220 mW of power if cable is unplugged .
2022-06-20 13:04:50 +02:00
* The workaround is only applicable to poll mode . Energy Detect Power - Down may
* not be used in interrupt mode lest link change detection becomes unreliable .
2012-11-15 09:00:57 +00:00
*/
2023-03-18 21:32:41 +01:00
int lan87xx_read_status ( struct phy_device * phydev )
2010-01-06 20:35:14 -08:00
{
2016-01-20 13:40:35 +01:00
struct smsc_phy_priv * priv = phydev - > priv ;
2023-03-11 19:34:45 +01:00
int err ;
2016-01-20 13:40:35 +01:00
2023-03-11 19:34:45 +01:00
err = genphy_read_status ( phydev ) ;
if ( err )
return err ;
2012-11-15 09:00:57 +00:00
2023-04-02 17:14:35 +02:00
if ( ! phydev - > link & & priv & & priv - > edpd_enable & &
priv - > edpd_max_wait_ms ) {
unsigned int max_wait = priv - > edpd_max_wait_ms * 1000 ;
int rc ;
2012-11-15 09:00:57 +00:00
/* Disable EDPD to wake up PHY */
2023-04-02 17:14:35 +02:00
rc = phy_read ( phydev , MII_LAN83C185_CTRL_STATUS ) ;
2012-11-15 09:00:57 +00:00
if ( rc < 0 )
return rc ;
rc = phy_write ( phydev , MII_LAN83C185_CTRL_STATUS ,
rc & ~ MII_LAN83C185_EDPWRDOWN ) ;
if ( rc < 0 )
return rc ;
2020-06-20 22:55:34 +08:00
/* Wait max 640 ms to detect energy and the timeout is not
* an actual error .
*/
read_poll_timeout ( phy_read , rc ,
rc & MII_LAN83C185_ENERGYON | | rc < 0 ,
2023-04-02 17:14:35 +02:00
10000 , max_wait , true , phydev ,
2020-06-20 22:55:34 +08:00
MII_LAN83C185_CTRL_STATUS ) ;
2020-03-23 23:05:59 +08:00
if ( rc < 0 )
return rc ;
2012-11-15 09:00:57 +00:00
/* Re-enable EDPD */
rc = phy_read ( phydev , MII_LAN83C185_CTRL_STATUS ) ;
if ( rc < 0 )
return rc ;
rc = phy_write ( phydev , MII_LAN83C185_CTRL_STATUS ,
rc | MII_LAN83C185_EDPWRDOWN ) ;
if ( rc < 0 )
return rc ;
}
return err ;
2010-01-06 20:35:14 -08:00
}
2023-03-18 21:32:41 +01:00
EXPORT_SYMBOL_GPL ( lan87xx_read_status ) ;
2006-05-07 23:22:53 +02:00
2023-07-25 16:54:30 -07:00
static int lan874x_phy_config_init ( struct phy_device * phydev )
{
u16 val ;
int rc ;
/* Setup LED2/nINT/nPME pin to function as nPME. May need user option
* to use LED1 / nINT / nPME .
*/
val = MII_LAN874X_PHY_PME2_SET ;
/* The bits MII_LAN874X_PHY_WOL_PFDA_FR, MII_LAN874X_PHY_WOL_WUFR,
* MII_LAN874X_PHY_WOL_MPR , and MII_LAN874X_PHY_WOL_BCAST_FR need to
* be cleared to de - assert PME signal after a WoL event happens , but
* using PME auto clear gets around that .
*/
val | = MII_LAN874X_PHY_PME_SELF_CLEAR ;
rc = phy_write_mmd ( phydev , MDIO_MMD_PCS , MII_LAN874X_PHY_MMD_WOL_WUCSR ,
val ) ;
if ( rc < 0 )
return rc ;
/* set nPME self clear delay time */
rc = phy_write_mmd ( phydev , MDIO_MMD_PCS , MII_LAN874X_PHY_MMD_MCFGR ,
MII_LAN874X_PHY_PME_SELF_CLEAR_DELAY ) ;
if ( rc < 0 )
return rc ;
return smsc_phy_config_init ( phydev ) ;
}
static void lan874x_get_wol ( struct phy_device * phydev ,
struct ethtool_wolinfo * wol )
{
struct smsc_phy_priv * priv = phydev - > priv ;
int rc ;
wol - > supported = ( WAKE_UCAST | WAKE_BCAST | WAKE_MAGIC |
WAKE_ARP | WAKE_MCAST ) ;
wol - > wolopts = 0 ;
rc = phy_read_mmd ( phydev , MDIO_MMD_PCS , MII_LAN874X_PHY_MMD_WOL_WUCSR ) ;
if ( rc < 0 )
return ;
if ( rc & MII_LAN874X_PHY_WOL_PFDAEN )
wol - > wolopts | = WAKE_UCAST ;
if ( rc & MII_LAN874X_PHY_WOL_BCSTEN )
wol - > wolopts | = WAKE_BCAST ;
if ( rc & MII_LAN874X_PHY_WOL_MPEN )
wol - > wolopts | = WAKE_MAGIC ;
if ( rc & MII_LAN874X_PHY_WOL_WUEN ) {
if ( priv - > wol_arp )
wol - > wolopts | = WAKE_ARP ;
else
wol - > wolopts | = WAKE_MCAST ;
}
}
static u16 smsc_crc16 ( const u8 * buffer , size_t len )
{
return bitrev16 ( crc16 ( 0xFFFF , buffer , len ) ) ;
}
static int lan874x_chk_wol_pattern ( const u8 pattern [ ] , const u16 * mask ,
u8 len , u8 * data , u8 * datalen )
{
size_t i , j , k ;
int ret = 0 ;
u16 bits ;
/* Pattern filtering can match up to 128 bytes of frame data. There
* are 8 registers to program the 16 - bit masks , where each bit means
* the byte will be compared . The frame data will then go through a
* CRC16 calculation for hardware comparison . This helper function
* makes sure only relevant frame data are included in this
* calculation . It provides a warning when the masks and expected
* data size do not match .
*/
i = 0 ;
k = 0 ;
while ( len > 0 ) {
bits = * mask ;
for ( j = 0 ; j < 16 ; j + + , i + + , len - - ) {
/* No more pattern. */
if ( ! len ) {
/* The rest of bitmap is not empty. */
if ( bits )
ret = i + 1 ;
break ;
}
if ( bits & 1 )
data [ k + + ] = pattern [ i ] ;
bits > > = 1 ;
}
mask + + ;
}
* datalen = k ;
return ret ;
}
static int lan874x_set_wol_pattern ( struct phy_device * phydev , u16 val ,
const u8 data [ ] , u8 datalen ,
const u16 * mask , u8 masklen )
{
u16 crc , reg ;
int rc ;
/* Starting pattern offset is set before calling this function. */
val | = MII_LAN874X_PHY_WOL_FILTER_EN ;
rc = phy_write_mmd ( phydev , MDIO_MMD_PCS ,
MII_LAN874X_PHY_MMD_WOL_WUF_CFGA , val ) ;
if ( rc < 0 )
return rc ;
crc = smsc_crc16 ( data , datalen ) ;
rc = phy_write_mmd ( phydev , MDIO_MMD_PCS ,
MII_LAN874X_PHY_MMD_WOL_WUF_CFGB , crc ) ;
if ( rc < 0 )
return rc ;
masklen = ( masklen + 15 ) & ~ 0xf ;
reg = MII_LAN874X_PHY_MMD_WOL_WUF_MASK7 ;
while ( masklen > = 16 ) {
rc = phy_write_mmd ( phydev , MDIO_MMD_PCS , reg , * mask ) ;
if ( rc < 0 )
return rc ;
reg - - ;
mask + + ;
masklen - = 16 ;
}
/* Clear out the rest of mask registers. */
while ( reg ! = MII_LAN874X_PHY_MMD_WOL_WUF_MASK0 ) {
phy_write_mmd ( phydev , MDIO_MMD_PCS , reg , 0 ) ;
reg - - ;
}
return rc ;
}
static int lan874x_set_wol ( struct phy_device * phydev ,
struct ethtool_wolinfo * wol )
{
struct net_device * ndev = phydev - > attached_dev ;
struct smsc_phy_priv * priv = phydev - > priv ;
u16 val , val_wucsr ;
u8 data [ 128 ] ;
u8 datalen ;
int rc ;
/* lan874x has only one WoL filter pattern */
if ( ( wol - > wolopts & ( WAKE_ARP | WAKE_MCAST ) ) = =
( WAKE_ARP | WAKE_MCAST ) ) {
phydev_info ( phydev ,
" lan874x WoL supports one of ARP|MCAST at a time \n " ) ;
return - EOPNOTSUPP ;
}
rc = phy_read_mmd ( phydev , MDIO_MMD_PCS , MII_LAN874X_PHY_MMD_WOL_WUCSR ) ;
if ( rc < 0 )
return rc ;
val_wucsr = rc ;
if ( wol - > wolopts & WAKE_UCAST )
val_wucsr | = MII_LAN874X_PHY_WOL_PFDAEN ;
else
val_wucsr & = ~ MII_LAN874X_PHY_WOL_PFDAEN ;
if ( wol - > wolopts & WAKE_BCAST )
val_wucsr | = MII_LAN874X_PHY_WOL_BCSTEN ;
else
val_wucsr & = ~ MII_LAN874X_PHY_WOL_BCSTEN ;
if ( wol - > wolopts & WAKE_MAGIC )
val_wucsr | = MII_LAN874X_PHY_WOL_MPEN ;
else
val_wucsr & = ~ MII_LAN874X_PHY_WOL_MPEN ;
/* Need to use pattern matching */
if ( wol - > wolopts & ( WAKE_ARP | WAKE_MCAST ) )
val_wucsr | = MII_LAN874X_PHY_WOL_WUEN ;
else
val_wucsr & = ~ MII_LAN874X_PHY_WOL_WUEN ;
if ( wol - > wolopts & WAKE_ARP ) {
const u8 pattern [ 2 ] = { 0x08 , 0x06 } ;
const u16 mask [ 1 ] = { 0x0003 } ;
rc = lan874x_chk_wol_pattern ( pattern , mask , 2 , data ,
& datalen ) ;
if ( rc )
phydev_dbg ( phydev , " pattern not valid at %d \n " , rc ) ;
/* Need to match broadcast destination address and provided
* data pattern at offset 12.
*/
val = 12 | MII_LAN874X_PHY_WOL_FILTER_BCSTEN ;
rc = lan874x_set_wol_pattern ( phydev , val , data , datalen , mask ,
2 ) ;
if ( rc < 0 )
return rc ;
priv - > wol_arp = true ;
}
if ( wol - > wolopts & WAKE_MCAST ) {
/* Need to match multicast destination address. */
val = MII_LAN874X_PHY_WOL_FILTER_MCASTTEN ;
rc = lan874x_set_wol_pattern ( phydev , val , data , 0 , NULL , 0 ) ;
if ( rc < 0 )
return rc ;
priv - > wol_arp = false ;
}
if ( wol - > wolopts & ( WAKE_MAGIC | WAKE_UCAST ) ) {
const u8 * mac = ( const u8 * ) ndev - > dev_addr ;
int i , reg ;
reg = MII_LAN874X_PHY_MMD_WOL_RX_ADDRC ;
for ( i = 0 ; i < 6 ; i + = 2 , reg - - ) {
rc = phy_write_mmd ( phydev , MDIO_MMD_PCS , reg ,
( ( mac [ i + 1 ] < < 8 ) | mac [ i ] ) ) ;
if ( rc < 0 )
return rc ;
}
}
rc = phy_write_mmd ( phydev , MDIO_MMD_PCS , MII_LAN874X_PHY_MMD_WOL_WUCSR ,
val_wucsr ) ;
if ( rc < 0 )
return rc ;
return 0 ;
}
2017-06-03 20:00:36 +02:00
static int smsc_get_sset_count ( struct phy_device * phydev )
{
return ARRAY_SIZE ( smsc_hw_stats ) ;
}
static void smsc_get_strings ( struct phy_device * phydev , u8 * data )
{
int i ;
for ( i = 0 ; i < ARRAY_SIZE ( smsc_hw_stats ) ; i + + ) {
2017-06-20 22:40:46 +02:00
strncpy ( data + i * ETH_GSTRING_LEN ,
2017-06-03 20:00:36 +02:00
smsc_hw_stats [ i ] . string , ETH_GSTRING_LEN ) ;
}
}
static u64 smsc_get_stat ( struct phy_device * phydev , int i )
{
struct smsc_hw_stat stat = smsc_hw_stats [ i ] ;
int val ;
u64 ret ;
val = phy_read ( phydev , stat . reg ) ;
if ( val < 0 )
2018-04-27 16:18:58 +08:00
ret = U64_MAX ;
2017-06-03 20:00:36 +02:00
else
ret = val ;
return ret ;
}
static void smsc_get_stats ( struct phy_device * phydev ,
struct ethtool_stats * stats , u64 * data )
{
int i ;
for ( i = 0 ; i < ARRAY_SIZE ( smsc_hw_stats ) ; i + + )
data [ i ] = smsc_get_stat ( phydev , i ) ;
}
2023-04-02 17:16:56 +02:00
static int smsc_phy_get_edpd ( struct phy_device * phydev , u16 * edpd )
{
struct smsc_phy_priv * priv = phydev - > priv ;
if ( ! priv )
return - EOPNOTSUPP ;
if ( ! priv - > edpd_enable )
* edpd = ETHTOOL_PHY_EDPD_DISABLE ;
else if ( ! priv - > edpd_max_wait_ms )
* edpd = ETHTOOL_PHY_EDPD_NO_TX ;
else
* edpd = PHY_STATE_MACH_MS + priv - > edpd_max_wait_ms ;
return 0 ;
}
static int smsc_phy_set_edpd ( struct phy_device * phydev , u16 edpd )
{
struct smsc_phy_priv * priv = phydev - > priv ;
if ( ! priv )
return - EOPNOTSUPP ;
switch ( edpd ) {
case ETHTOOL_PHY_EDPD_DISABLE :
priv - > edpd_enable = false ;
break ;
case ETHTOOL_PHY_EDPD_NO_TX :
priv - > edpd_enable = true ;
priv - > edpd_max_wait_ms = 0 ;
break ;
case ETHTOOL_PHY_EDPD_DFLT_TX_MSECS :
edpd = PHY_STATE_MACH_MS + EDPD_MAX_WAIT_DFLT_MS ;
fallthrough ;
default :
if ( phydev - > irq ! = PHY_POLL )
return - EOPNOTSUPP ;
if ( edpd < PHY_STATE_MACH_MS | | edpd > PHY_STATE_MACH_MS + 1000 )
return - EINVAL ;
priv - > edpd_enable = true ;
priv - > edpd_max_wait_ms = edpd - PHY_STATE_MACH_MS ;
}
priv - > edpd_mode_set_by_user = true ;
return smsc_phy_config_edpd ( phydev ) ;
}
int smsc_phy_get_tunable ( struct phy_device * phydev ,
struct ethtool_tunable * tuna , void * data )
{
switch ( tuna - > id ) {
case ETHTOOL_PHY_EDPD :
return smsc_phy_get_edpd ( phydev , data ) ;
default :
return - EOPNOTSUPP ;
}
}
EXPORT_SYMBOL_GPL ( smsc_phy_get_tunable ) ;
int smsc_phy_set_tunable ( struct phy_device * phydev ,
struct ethtool_tunable * tuna , const void * data )
{
switch ( tuna - > id ) {
case ETHTOOL_PHY_EDPD :
return smsc_phy_set_edpd ( phydev , * ( u16 * ) data ) ;
default :
return - EOPNOTSUPP ;
}
}
EXPORT_SYMBOL_GPL ( smsc_phy_set_tunable ) ;
2023-03-18 21:32:41 +01:00
int smsc_phy_probe ( struct phy_device * phydev )
2016-01-20 13:40:35 +01:00
{
struct device * dev = & phydev - > mdio . dev ;
struct smsc_phy_priv * priv ;
2022-08-28 19:26:55 +02:00
struct clk * refclk ;
2016-01-20 13:40:35 +01:00
priv = devm_kzalloc ( dev , sizeof ( * priv ) , GFP_KERNEL ) ;
if ( ! priv )
return - ENOMEM ;
2023-04-02 17:11:40 +02:00
priv - > edpd_enable = true ;
2023-04-02 17:14:35 +02:00
priv - > edpd_max_wait_ms = EDPD_MAX_WAIT_DFLT_MS ;
2016-01-20 13:40:35 +01:00
2023-03-08 21:34:13 +01:00
if ( device_property_present ( dev , " smsc,disable-energy-detect " ) )
2023-04-02 17:11:40 +02:00
priv - > edpd_enable = false ;
2016-01-20 13:40:35 +01:00
phydev - > priv = priv ;
2020-09-09 15:45:00 +02:00
/* Make clk optional to keep DTB backward compatibility. */
2022-08-28 19:26:55 +02:00
refclk = devm_clk_get_optional_enabled ( dev , NULL ) ;
if ( IS_ERR ( refclk ) )
return dev_err_probe ( dev , PTR_ERR ( refclk ) ,
2021-01-11 09:59:32 +01:00
" Failed to request clock \n " ) ;
2020-09-09 15:45:00 +02:00
2022-08-28 19:26:55 +02:00
return clk_set_rate ( refclk , 50 * 1000 * 1000 ) ;
2016-01-20 13:40:35 +01:00
}
2023-03-18 21:32:41 +01:00
EXPORT_SYMBOL_GPL ( smsc_phy_probe ) ;
2016-01-20 13:40:35 +01:00
2012-07-04 05:44:34 +00:00
static struct phy_driver smsc_phy_driver [ ] = {
{
2006-05-07 23:22:53 +02:00
. phy_id = 0x0007c0a0 , /* OUI=0x00800f, Model#=0x0a */
. phy_id_mask = 0xfffffff0 ,
. name = " SMSC LAN83C185 " ,
2019-04-12 20:47:03 +02:00
/* PHY_BASIC_FEATURES */
2006-05-07 23:22:53 +02:00
2016-01-20 13:40:35 +01:00
. probe = smsc_phy_probe ,
2006-05-07 23:22:53 +02:00
/* basic functions */
2008-04-28 18:36:46 +01:00
. config_init = smsc_phy_config_init ,
2014-08-15 15:00:38 +02:00
. soft_reset = smsc_phy_reset ,
2006-05-07 23:22:53 +02:00
/* IRQ related */
2008-04-28 18:36:46 +01:00
. config_intr = smsc_phy_config_intr ,
2020-11-13 18:52:21 +02:00
. handle_interrupt = smsc_phy_handle_interrupt ,
2006-05-07 23:22:53 +02:00
2009-01-22 14:07:43 -08:00
. suspend = genphy_suspend ,
. resume = genphy_resume ,
2012-07-04 05:44:34 +00:00
} , {
2008-04-28 18:37:29 +01:00
. phy_id = 0x0007c0b0 , /* OUI=0x00800f, Model#=0x0b */
. phy_id_mask = 0xfffffff0 ,
. name = " SMSC LAN8187 " ,
2019-04-12 20:47:03 +02:00
/* PHY_BASIC_FEATURES */
2008-04-28 18:37:29 +01:00
2016-01-20 13:40:35 +01:00
. probe = smsc_phy_probe ,
2008-04-28 18:37:29 +01:00
/* basic functions */
. config_init = smsc_phy_config_init ,
2014-08-15 15:00:38 +02:00
. soft_reset = smsc_phy_reset ,
2008-04-28 18:37:29 +01:00
/* IRQ related */
. config_intr = smsc_phy_config_intr ,
2020-11-13 18:52:21 +02:00
. handle_interrupt = smsc_phy_handle_interrupt ,
2008-04-28 18:37:29 +01:00
2017-06-03 20:00:36 +02:00
/* Statistics */
. get_sset_count = smsc_get_sset_count ,
. get_strings = smsc_get_strings ,
. get_stats = smsc_get_stats ,
2009-01-22 14:07:43 -08:00
. suspend = genphy_suspend ,
. resume = genphy_resume ,
2012-07-04 05:44:34 +00:00
} , {
2020-08-26 13:17:17 +02:00
/* This covers internal PHY (phy_id: 0x0007C0C3) for
* LAN9500 ( PID : 0x9500 ) , LAN9514 ( PID : 0xec00 ) , LAN9505 ( PID : 0x9505 )
*/
2008-04-28 18:37:29 +01:00
. phy_id = 0x0007c0c0 , /* OUI=0x00800f, Model#=0x0c */
. phy_id_mask = 0xfffffff0 ,
. name = " SMSC LAN8700 " ,
2019-04-12 20:47:03 +02:00
/* PHY_BASIC_FEATURES */
2008-04-28 18:37:29 +01:00
2016-01-20 13:40:35 +01:00
. probe = smsc_phy_probe ,
2008-04-28 18:37:29 +01:00
/* basic functions */
2015-08-14 20:11:02 +03:00
. read_status = lan87xx_read_status ,
2008-04-28 18:37:29 +01:00
. config_init = smsc_phy_config_init ,
2014-08-15 15:00:38 +02:00
. soft_reset = smsc_phy_reset ,
2020-08-26 13:17:17 +02:00
. config_aneg = lan87xx_config_aneg ,
2008-04-28 18:37:29 +01:00
/* IRQ related */
. config_intr = smsc_phy_config_intr ,
2020-11-13 18:52:21 +02:00
. handle_interrupt = smsc_phy_handle_interrupt ,
2008-04-28 18:37:29 +01:00
2017-06-03 20:00:36 +02:00
/* Statistics */
. get_sset_count = smsc_get_sset_count ,
. get_strings = smsc_get_strings ,
. get_stats = smsc_get_stats ,
2023-04-02 17:17:35 +02:00
. get_tunable = smsc_phy_get_tunable ,
. set_tunable = smsc_phy_set_tunable ,
2009-01-22 14:07:43 -08:00
. suspend = genphy_suspend ,
. resume = genphy_resume ,
2012-07-04 05:44:34 +00:00
} , {
2008-11-05 00:35:37 +00:00
. phy_id = 0x0007c0d0 , /* OUI=0x00800f, Model#=0x0d */
. phy_id_mask = 0xfffffff0 ,
. name = " SMSC LAN911x Internal PHY " ,
2019-04-12 20:47:03 +02:00
/* PHY_BASIC_FEATURES */
2008-11-05 00:35:37 +00:00
2016-01-20 13:40:35 +01:00
. probe = smsc_phy_probe ,
2008-11-05 00:35:37 +00:00
/* IRQ related */
. config_intr = smsc_phy_config_intr ,
2020-11-13 18:52:21 +02:00
. handle_interrupt = smsc_phy_handle_interrupt ,
2008-11-05 00:35:37 +00:00
2009-01-22 14:07:43 -08:00
. suspend = genphy_suspend ,
. resume = genphy_resume ,
2012-07-04 05:44:34 +00:00
} , {
2020-08-26 13:17:17 +02:00
/* This covers internal PHY (phy_id: 0x0007C0F0) for
* LAN9500A ( PID : 0x9E00 ) , LAN9505A ( PID : 0x9E01 )
*/
2009-03-23 15:17:31 -07:00
. phy_id = 0x0007c0f0 , /* OUI=0x00800f, Model#=0x0f */
. phy_id_mask = 0xfffffff0 ,
. name = " SMSC LAN8710/LAN8720 " ,
2019-04-12 20:47:03 +02:00
/* PHY_BASIC_FEATURES */
2009-03-23 15:17:31 -07:00
2016-01-20 13:40:35 +01:00
. probe = smsc_phy_probe ,
2009-03-23 15:17:31 -07:00
/* basic functions */
2012-11-15 09:00:57 +00:00
. read_status = lan87xx_read_status ,
2012-12-06 15:16:02 +00:00
. config_init = smsc_phy_config_init ,
2014-08-15 15:00:38 +02:00
. soft_reset = smsc_phy_reset ,
2021-03-29 11:45:36 +02:00
. config_aneg = lan95xx_config_aneg_ext ,
2009-03-23 15:17:31 -07:00
/* IRQ related */
. config_intr = smsc_phy_config_intr ,
2020-11-13 18:52:21 +02:00
. handle_interrupt = smsc_phy_handle_interrupt ,
2009-03-23 15:17:31 -07:00
2017-06-03 20:00:36 +02:00
/* Statistics */
. get_sset_count = smsc_get_sset_count ,
. get_strings = smsc_get_strings ,
. get_stats = smsc_get_stats ,
2023-04-02 17:17:35 +02:00
. get_tunable = smsc_phy_get_tunable ,
. set_tunable = smsc_phy_set_tunable ,
2016-01-09 04:54:21 -07:00
. suspend = genphy_suspend ,
. resume = genphy_resume ,
} , {
. phy_id = 0x0007c110 ,
. phy_id_mask = 0xfffffff0 ,
. name = " SMSC LAN8740 " ,
2019-04-12 20:47:03 +02:00
/* PHY_BASIC_FEATURES */
2019-10-23 11:44:24 +02:00
. flags = PHY_RST_AFTER_CLK_EN ,
2016-01-09 04:54:21 -07:00
2016-01-20 13:40:35 +01:00
. probe = smsc_phy_probe ,
2016-01-09 04:54:21 -07:00
/* basic functions */
. read_status = lan87xx_read_status ,
2023-07-25 16:54:30 -07:00
. config_init = lan874x_phy_config_init ,
2016-01-09 04:54:21 -07:00
. soft_reset = smsc_phy_reset ,
/* IRQ related */
. config_intr = smsc_phy_config_intr ,
2020-11-13 18:52:21 +02:00
. handle_interrupt = smsc_phy_handle_interrupt ,
2016-01-09 04:54:21 -07:00
2017-06-03 20:00:36 +02:00
/* Statistics */
. get_sset_count = smsc_get_sset_count ,
. get_strings = smsc_get_strings ,
. get_stats = smsc_get_stats ,
2023-04-02 17:17:35 +02:00
. get_tunable = smsc_phy_get_tunable ,
. set_tunable = smsc_phy_set_tunable ,
2023-07-25 16:54:30 -07:00
/* WoL */
. set_wol = lan874x_set_wol ,
. get_wol = lan874x_get_wol ,
2022-05-05 11:12:52 -07:00
. suspend = genphy_suspend ,
. resume = genphy_resume ,
} , {
2022-05-09 11:58:04 -07:00
. phy_id = 0x0007c130 , /* 0x0007c130 and 0x0007c131 */
/* This mask (0xfffffff2) is to differentiate from
* LAN88xx ( phy_id 0x0007c132 )
* and allows future phy_id revisions .
*/
2022-05-05 11:12:52 -07:00
. phy_id_mask = 0xfffffff2 ,
. name = " Microchip LAN8742 " ,
/* PHY_BASIC_FEATURES */
. flags = PHY_RST_AFTER_CLK_EN ,
. probe = smsc_phy_probe ,
/* basic functions */
. read_status = lan87xx_read_status ,
2023-07-25 16:54:30 -07:00
. config_init = lan874x_phy_config_init ,
2022-05-05 11:12:52 -07:00
. soft_reset = smsc_phy_reset ,
/* IRQ related */
. config_intr = smsc_phy_config_intr ,
. handle_interrupt = smsc_phy_handle_interrupt ,
/* Statistics */
. get_sset_count = smsc_get_sset_count ,
. get_strings = smsc_get_strings ,
. get_stats = smsc_get_stats ,
2023-04-02 17:17:35 +02:00
. get_tunable = smsc_phy_get_tunable ,
. set_tunable = smsc_phy_set_tunable ,
2023-07-25 16:54:30 -07:00
/* WoL */
. set_wol = lan874x_set_wol ,
. get_wol = lan874x_get_wol ,
2009-03-23 15:17:31 -07:00
. suspend = genphy_suspend ,
. resume = genphy_resume ,
2012-07-04 05:44:34 +00:00
} } ;
2009-03-23 15:17:31 -07:00
2014-11-11 19:45:59 +01:00
module_phy_driver ( smsc_phy_driver ) ;
2006-05-07 23:22:53 +02:00
MODULE_DESCRIPTION ( " SMSC PHY driver " ) ;
MODULE_AUTHOR ( " Herbert Valerio Riedel " ) ;
MODULE_LICENSE ( " GPL " ) ;
2010-10-03 23:43:32 +00:00
static struct mdio_device_id __maybe_unused smsc_tbl [ ] = {
2010-04-02 01:05:56 +00:00
{ 0x0007c0a0 , 0xfffffff0 } ,
{ 0x0007c0b0 , 0xfffffff0 } ,
{ 0x0007c0c0 , 0xfffffff0 } ,
{ 0x0007c0d0 , 0xfffffff0 } ,
{ 0x0007c0f0 , 0xfffffff0 } ,
2016-01-09 04:54:21 -07:00
{ 0x0007c110 , 0xfffffff0 } ,
2022-05-05 11:12:52 -07:00
{ 0x0007c130 , 0xfffffff2 } ,
2010-04-02 01:05:56 +00:00
{ }
} ;
MODULE_DEVICE_TABLE ( mdio , smsc_tbl ) ;