2019-01-21 19:05:50 +01:00
// SPDX-License-Identifier: GPL-2.0+
2008-02-03 03:50:54 -08:00
/*
* drivers / net / phy / realtek . c
*
* Driver for Realtek PHYs
*
* Author : Johnson Leung < r58129 @ freescale . com >
*
* Copyright ( c ) 2004 Freescale Semiconductor , Inc .
*/
2017-12-02 22:51:24 +01:00
# include <linux/bitops.h>
2008-02-03 03:50:54 -08:00
# include <linux/phy.h>
2011-07-03 15:21:01 -04:00
# include <linux/module.h>
2008-02-03 03:50:54 -08:00
2017-12-02 22:51:27 +01:00
# define RTL821x_PHYSR 0x11
# define RTL821x_PHYSR_DUPLEX BIT(13)
# define RTL821x_PHYSR_SPEED GENMASK(15, 14)
2017-12-02 22:51:26 +01:00
2017-12-02 22:51:27 +01:00
# define RTL821x_INER 0x12
# define RTL8211B_INER_INIT 0x6400
# define RTL8211E_INER_LINK_STATUS BIT(10)
# define RTL8211F_INER_LINK_STATUS BIT(4)
2017-12-02 22:51:26 +01:00
2017-12-02 22:51:27 +01:00
# define RTL821x_INSR 0x13
2017-12-02 22:51:26 +01:00
2019-05-09 00:51:15 +03:00
# define RTL821x_EXT_PAGE_SELECT 0x1e
2017-12-02 22:51:27 +01:00
# define RTL821x_PAGE_SELECT 0x1f
2008-02-03 03:50:54 -08:00
2017-12-02 22:51:27 +01:00
# define RTL8211F_INSR 0x1d
2013-01-23 00:30:03 +00:00
2017-12-02 22:51:27 +01:00
# define RTL8211F_TX_DELAY BIT(8)
2019-05-09 00:51:15 +03:00
# define RTL8211E_TX_DELAY BIT(1)
# define RTL8211E_RX_DELAY BIT(2)
# define RTL8211E_MODE_MII_GMII BIT(3)
2017-12-02 22:51:27 +01:00
# define RTL8201F_ISR 0x1e
# define RTL8201F_IER 0x13
2017-09-12 18:54:36 +09:00
2018-07-14 11:45:53 +02:00
# define RTL8366RB_POWER_SAVE 0x15
# define RTL8366RB_POWER_SAVE_ON BIT(12)
2008-02-03 03:50:54 -08:00
MODULE_DESCRIPTION ( " Realtek PHY driver " ) ;
MODULE_AUTHOR ( " Johnson Leung " ) ;
MODULE_LICENSE ( " GPL " ) ;
2018-01-12 23:17:34 +01:00
static int rtl821x_read_page ( struct phy_device * phydev )
2017-12-02 22:51:28 +01:00
{
2018-01-12 23:17:34 +01:00
return __phy_read ( phydev , RTL821x_PAGE_SELECT ) ;
2017-12-02 22:51:28 +01:00
}
2018-01-12 23:17:34 +01:00
static int rtl821x_write_page ( struct phy_device * phydev , int page )
2017-12-02 22:51:28 +01:00
{
2018-01-12 23:17:34 +01:00
return __phy_write ( phydev , RTL821x_PAGE_SELECT , page ) ;
2017-12-02 22:51:28 +01:00
}
2017-09-12 18:54:36 +09:00
static int rtl8201_ack_interrupt ( struct phy_device * phydev )
{
int err ;
err = phy_read ( phydev , RTL8201F_ISR ) ;
return ( err < 0 ) ? err : 0 ;
}
2008-02-03 03:50:54 -08:00
static int rtl821x_ack_interrupt ( struct phy_device * phydev )
{
int err ;
err = phy_read ( phydev , RTL821x_INSR ) ;
return ( err < 0 ) ? err : 0 ;
}
2015-06-18 16:42:47 +08:00
static int rtl8211f_ack_interrupt ( struct phy_device * phydev )
{
int err ;
2018-01-12 23:17:34 +01:00
err = phy_read_paged ( phydev , 0xa43 , RTL8211F_INSR ) ;
2015-06-18 16:42:47 +08:00
return ( err < 0 ) ? err : 0 ;
}
2017-09-12 18:54:36 +09:00
static int rtl8201_config_intr ( struct phy_device * phydev )
{
2017-12-02 22:51:28 +01:00
u16 val ;
2017-09-12 18:54:36 +09:00
if ( phydev - > interrupts = = PHY_INTERRUPT_ENABLED )
2017-12-02 22:51:28 +01:00
val = BIT ( 13 ) | BIT ( 12 ) | BIT ( 11 ) ;
2017-09-12 18:54:36 +09:00
else
2017-12-02 22:51:28 +01:00
val = 0 ;
2017-09-12 18:54:36 +09:00
2018-01-12 23:17:34 +01:00
return phy_write_paged ( phydev , 0x7 , RTL8201F_IER , val ) ;
2017-09-12 18:54:36 +09:00
}
2013-01-23 00:30:03 +00:00
static int rtl8211b_config_intr ( struct phy_device * phydev )
2008-02-03 03:50:54 -08:00
{
int err ;
if ( phydev - > interrupts = = PHY_INTERRUPT_ENABLED )
err = phy_write ( phydev , RTL821x_INER ,
2017-12-02 22:51:25 +01:00
RTL8211B_INER_INIT ) ;
2008-02-03 03:50:54 -08:00
else
err = phy_write ( phydev , RTL821x_INER , 0 ) ;
return err ;
}
2013-01-23 00:30:03 +00:00
static int rtl8211e_config_intr ( struct phy_device * phydev )
{
int err ;
if ( phydev - > interrupts = = PHY_INTERRUPT_ENABLED )
err = phy_write ( phydev , RTL821x_INER ,
2013-08-19 08:48:34 +02:00
RTL8211E_INER_LINK_STATUS ) ;
2013-01-23 00:30:03 +00:00
else
err = phy_write ( phydev , RTL821x_INER , 0 ) ;
return err ;
}
2015-06-18 16:42:47 +08:00
static int rtl8211f_config_intr ( struct phy_device * phydev )
{
2017-12-02 22:51:28 +01:00
u16 val ;
2015-06-18 16:42:47 +08:00
if ( phydev - > interrupts = = PHY_INTERRUPT_ENABLED )
2017-12-02 22:51:28 +01:00
val = RTL8211F_INER_LINK_STATUS ;
2015-06-18 16:42:47 +08:00
else
2017-12-02 22:51:28 +01:00
val = 0 ;
2015-06-18 16:42:47 +08:00
2018-01-12 23:17:34 +01:00
return phy_write_paged ( phydev , 0xa42 , RTL821x_INER , val ) ;
2015-06-18 16:42:47 +08:00
}
2018-06-28 20:46:45 +02:00
static int rtl8211_config_aneg ( struct phy_device * phydev )
{
int ret ;
ret = genphy_config_aneg ( phydev ) ;
if ( ret < 0 )
return ret ;
/* Quirk was copied from vendor driver. Unfortunately it includes no
* description of the magic numbers .
*/
if ( phydev - > speed = = SPEED_100 & & phydev - > autoneg = = AUTONEG_DISABLE ) {
phy_write ( phydev , 0x17 , 0x2138 ) ;
phy_write ( phydev , 0x0e , 0x0260 ) ;
} else {
phy_write ( phydev , 0x17 , 0x2108 ) ;
phy_write ( phydev , 0x0e , 0x0000 ) ;
}
return 0 ;
}
2018-07-02 08:08:13 +02:00
static int rtl8211c_config_init ( struct phy_device * phydev )
{
/* RTL8211C has an issue when operating in Gigabit slave mode */
2019-03-27 22:00:32 +01:00
return phy_set_bits ( phydev , MII_CTRL1000 ,
CTL1000_ENABLE_MASTER | CTL1000_AS_MASTER ) ;
2018-07-02 08:08:13 +02:00
}
2015-06-18 16:42:47 +08:00
static int rtl8211f_config_init ( struct phy_device * phydev )
{
2019-05-09 00:51:17 +03:00
u16 val ;
2015-06-18 16:42:47 +08:00
2019-05-09 00:51:17 +03:00
/* enable TX-delay for rgmii-{id,txid}, and disable it for rgmii and
* rgmii - rxid . The RX - delay can be enabled by the external RXDLY pin .
*/
switch ( phydev - > interface ) {
case PHY_INTERFACE_MODE_RGMII :
case PHY_INTERFACE_MODE_RGMII_RXID :
val = 0 ;
break ;
case PHY_INTERFACE_MODE_RGMII_ID :
case PHY_INTERFACE_MODE_RGMII_TXID :
2018-01-12 23:17:34 +01:00
val = RTL8211F_TX_DELAY ;
2019-05-09 00:51:17 +03:00
break ;
default : /* the rest of the modes imply leaving delay as is. */
return 0 ;
}
2015-06-18 16:42:47 +08:00
2018-01-12 23:17:34 +01:00
return phy_modify_paged ( phydev , 0xd08 , 0x11 , RTL8211F_TX_DELAY , val ) ;
2015-06-18 16:42:47 +08:00
}
2019-05-09 00:51:15 +03:00
static int rtl8211e_config_init ( struct phy_device * phydev )
{
int ret = 0 , oldpage ;
u16 val ;
/* enable TX/RX delay for rgmii-* modes, and disable them for rgmii. */
switch ( phydev - > interface ) {
case PHY_INTERFACE_MODE_RGMII :
val = 0 ;
break ;
case PHY_INTERFACE_MODE_RGMII_ID :
val = RTL8211E_TX_DELAY | RTL8211E_RX_DELAY ;
break ;
case PHY_INTERFACE_MODE_RGMII_RXID :
val = RTL8211E_RX_DELAY ;
break ;
case PHY_INTERFACE_MODE_RGMII_TXID :
val = RTL8211E_TX_DELAY ;
break ;
default : /* the rest of the modes imply leaving delays as is. */
return 0 ;
}
/* According to a sample driver there is a 0x1c config register on the
* 0xa4 extension page ( 0x7 ) layout . It can be used to disable / enable
* the RX / TX delays otherwise controlled by RXDLY / TXDLY pins . It can
* also be used to customize the whole configuration register :
* 8 : 6 = PHY Address , 5 : 4 = Auto - Negotiation , 3 = Interface Mode Select ,
* 2 = RX Delay , 1 = TX Delay , 0 = SELRGV ( see original PHY datasheet
* for details ) .
*/
oldpage = phy_select_page ( phydev , 0x7 ) ;
if ( oldpage < 0 )
goto err_restore_page ;
ret = phy_write ( phydev , RTL821x_EXT_PAGE_SELECT , 0xa4 ) ;
if ( ret )
goto err_restore_page ;
ret = phy_modify ( phydev , 0x1c , RTL8211E_TX_DELAY | RTL8211E_RX_DELAY ,
val ) ;
err_restore_page :
return phy_restore_page ( phydev , oldpage , ret ) ;
}
2018-05-24 22:40:12 +02:00
static int rtl8211b_suspend ( struct phy_device * phydev )
{
phy_write ( phydev , MII_MMD_DATA , BIT ( 9 ) ) ;
return genphy_suspend ( phydev ) ;
}
static int rtl8211b_resume ( struct phy_device * phydev )
{
phy_write ( phydev , MII_MMD_DATA , 0 ) ;
return genphy_resume ( phydev ) ;
}
2018-07-14 11:45:53 +02:00
static int rtl8366rb_config_init ( struct phy_device * phydev )
{
int ret ;
ret = phy_set_bits ( phydev , RTL8366RB_POWER_SAVE ,
RTL8366RB_POWER_SAVE_ON ) ;
if ( ret ) {
dev_err ( & phydev - > mdio . dev ,
" error enabling power management \n " ) ;
}
return ret ;
}
2014-06-10 12:50:12 +09:00
static struct phy_driver realtek_drvs [ ] = {
{
2018-11-10 00:40:37 +01:00
PHY_ID_MATCH_EXACT ( 0x00008201 ) ,
2014-06-10 12:50:12 +09:00
. name = " RTL8201CP Ethernet " ,
2017-09-12 18:54:36 +09:00
} , {
2018-11-10 00:40:37 +01:00
PHY_ID_MATCH_EXACT ( 0x001cc816 ) ,
2018-11-04 19:02:42 +01:00
. name = " RTL8201F Fast Ethernet " ,
2017-09-12 18:54:36 +09:00
. ack_interrupt = & rtl8201_ack_interrupt ,
. config_intr = & rtl8201_config_intr ,
. suspend = genphy_suspend ,
. resume = genphy_resume ,
2018-01-12 23:17:34 +01:00
. read_page = rtl821x_read_page ,
. write_page = rtl821x_write_page ,
2018-06-28 20:46:45 +02:00
} , {
2018-11-10 00:40:37 +01:00
PHY_ID_MATCH_EXACT ( 0x001cc910 ) ,
2018-06-28 20:46:45 +02:00
. name = " RTL8211 Gigabit Ethernet " ,
. config_aneg = rtl8211_config_aneg ,
. read_mmd = & genphy_read_mmd_unsupported ,
. write_mmd = & genphy_write_mmd_unsupported ,
2014-06-10 12:50:12 +09:00
} , {
2018-11-10 00:40:37 +01:00
PHY_ID_MATCH_EXACT ( 0x001cc912 ) ,
2014-06-10 12:50:12 +09:00
. name = " RTL8211B Gigabit Ethernet " ,
. ack_interrupt = & rtl821x_ack_interrupt ,
. config_intr = & rtl8211b_config_intr ,
2018-03-20 09:44:53 +08:00
. read_mmd = & genphy_read_mmd_unsupported ,
. write_mmd = & genphy_write_mmd_unsupported ,
2018-05-24 22:40:12 +02:00
. suspend = rtl8211b_suspend ,
. resume = rtl8211b_resume ,
2018-07-02 08:08:13 +02:00
} , {
2018-11-10 00:40:37 +01:00
PHY_ID_MATCH_EXACT ( 0x001cc913 ) ,
2018-07-02 08:08:13 +02:00
. name = " RTL8211C Gigabit Ethernet " ,
. config_init = rtl8211c_config_init ,
. read_mmd = & genphy_read_mmd_unsupported ,
. write_mmd = & genphy_write_mmd_unsupported ,
2015-08-06 19:03:35 +08:00
} , {
2018-11-10 00:40:37 +01:00
PHY_ID_MATCH_EXACT ( 0x001cc914 ) ,
2015-08-06 19:03:35 +08:00
. name = " RTL8211DN Gigabit Ethernet " ,
. ack_interrupt = rtl821x_ack_interrupt ,
. config_intr = rtl8211e_config_intr ,
. suspend = genphy_suspend ,
. resume = genphy_resume ,
2014-06-10 12:50:12 +09:00
} , {
2018-11-10 00:40:37 +01:00
PHY_ID_MATCH_EXACT ( 0x001cc915 ) ,
2014-06-10 12:50:12 +09:00
. name = " RTL8211E Gigabit Ethernet " ,
2019-05-09 00:51:15 +03:00
. config_init = & rtl8211e_config_init ,
2014-06-10 12:50:12 +09:00
. ack_interrupt = & rtl821x_ack_interrupt ,
. config_intr = & rtl8211e_config_intr ,
. suspend = genphy_suspend ,
. resume = genphy_resume ,
2015-06-18 16:42:47 +08:00
} , {
2018-11-10 00:40:37 +01:00
PHY_ID_MATCH_EXACT ( 0x001cc916 ) ,
2015-06-18 16:42:47 +08:00
. name = " RTL8211F Gigabit Ethernet " ,
. config_init = & rtl8211f_config_init ,
. ack_interrupt = & rtl8211f_ack_interrupt ,
. config_intr = & rtl8211f_config_intr ,
. suspend = genphy_suspend ,
. resume = genphy_resume ,
2018-01-12 23:17:34 +01:00
. read_page = rtl821x_read_page ,
. write_page = rtl821x_write_page ,
2019-02-03 16:07:33 +01:00
} , {
PHY_ID_MATCH_EXACT ( 0x001cc800 ) ,
. name = " Generic Realtek PHY " ,
. suspend = genphy_suspend ,
. resume = genphy_resume ,
. read_page = rtl821x_read_page ,
. write_page = rtl821x_write_page ,
2018-07-14 11:45:53 +02:00
} , {
2018-11-10 00:40:37 +01:00
PHY_ID_MATCH_EXACT ( 0x001cc961 ) ,
2018-07-14 11:45:53 +02:00
. name = " RTL8366RB Gigabit Ethernet " ,
. config_init = & rtl8366rb_config_init ,
2019-02-24 01:11:15 +01:00
/* These interrupts are handled by the irq controller
* embedded inside the RTL8366RB , they get unmasked when the
* irq is requested and ACKed by reading the status register ,
* which is done by the irqchip code .
*/
. ack_interrupt = genphy_no_ack_interrupt ,
. config_intr = genphy_no_config_intr ,
2018-07-14 11:45:53 +02:00
. suspend = genphy_suspend ,
. resume = genphy_resume ,
2014-06-10 12:50:12 +09:00
} ,
2008-02-03 03:50:54 -08:00
} ;
2014-11-11 19:45:59 +01:00
module_phy_driver ( realtek_drvs ) ;
2010-04-02 01:05:56 +00:00
2018-11-07 08:52:46 +01:00
static const struct mdio_device_id __maybe_unused realtek_tbl [ ] = {
2018-11-10 00:40:37 +01:00
{ PHY_ID_MATCH_VENDOR ( 0x001cc800 ) } ,
2010-04-02 01:05:56 +00:00
{ }
} ;
MODULE_DEVICE_TABLE ( mdio , realtek_tbl ) ;