2012-10-14 19:07:16 +00:00
/*
* drivers / net / phy / at803x . c
*
* Driver for Atheros 803 x PHY
*
* Author : Matus Ujhelyi < ujhelyi . m @ gmail . com >
*
* 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/phy.h>
# include <linux/module.h>
# include <linux/string.h>
# include <linux/netdevice.h>
# include <linux/etherdevice.h>
2014-06-18 11:01:43 +02:00
# include <linux/of_gpio.h>
# include <linux/gpio/consumer.h>
2012-10-14 19:07:16 +00:00
# define AT803X_INTR_ENABLE 0x12
2016-01-15 01:55:24 +01:00
# define AT803X_INTR_ENABLE_AUTONEG_ERR BIT(15)
# define AT803X_INTR_ENABLE_SPEED_CHANGED BIT(14)
# define AT803X_INTR_ENABLE_DUPLEX_CHANGED BIT(13)
# define AT803X_INTR_ENABLE_PAGE_RECEIVED BIT(12)
# define AT803X_INTR_ENABLE_LINK_FAIL BIT(11)
# define AT803X_INTR_ENABLE_LINK_SUCCESS BIT(10)
# define AT803X_INTR_ENABLE_WIRESPEED_DOWNGRADE BIT(5)
# define AT803X_INTR_ENABLE_POLARITY_CHANGED BIT(1)
# define AT803X_INTR_ENABLE_WOL BIT(0)
2012-10-14 19:07:16 +00:00
# define AT803X_INTR_STATUS 0x13
2016-01-15 01:55:23 +01:00
2014-06-18 11:01:43 +02:00
# define AT803X_SMART_SPEED 0x14
# define AT803X_LED_CONTROL 0x18
2016-01-15 01:55:23 +01:00
2012-10-14 19:07:16 +00:00
# define AT803X_DEVICE_ADDR 0x03
# define AT803X_LOC_MAC_ADDR_0_15_OFFSET 0x804C
# define AT803X_LOC_MAC_ADDR_16_31_OFFSET 0x804B
# define AT803X_LOC_MAC_ADDR_32_47_OFFSET 0x804A
# define AT803X_MMD_ACCESS_CONTROL 0x0D
# define AT803X_MMD_ACCESS_CONTROL_DATA 0x0E
# define AT803X_FUNC_DATA 0x4003
2016-10-24 12:40:54 +02:00
# define AT803X_REG_CHIP_CONFIG 0x1f
# define AT803X_BT_BX_REG_SEL 0x8000
2016-01-15 01:55:23 +01:00
2013-06-03 20:10:06 +00:00
# define AT803X_DEBUG_ADDR 0x1D
# define AT803X_DEBUG_DATA 0x1E
2016-01-15 01:55:23 +01:00
2016-10-24 12:40:54 +02:00
# define AT803X_MODE_CFG_MASK 0x0F
# define AT803X_MODE_CFG_SGMII 0x01
# define AT803X_PSSR 0x11 /*PHY-Specific Status Register*/
# define AT803X_PSSR_MR_AN_COMPLETE 0x0200
2016-01-15 01:55:22 +01:00
# define AT803X_DEBUG_REG_0 0x00
# define AT803X_DEBUG_RX_CLK_DLY_EN BIT(15)
2016-01-15 01:55:23 +01:00
2016-01-15 01:55:22 +01:00
# define AT803X_DEBUG_REG_5 0x05
# define AT803X_DEBUG_TX_CLK_DLY_EN BIT(8)
2012-10-14 19:07:16 +00:00
2014-06-18 11:01:42 +02:00
# define ATH8030_PHY_ID 0x004dd076
# define ATH8031_PHY_ID 0x004dd074
# define ATH8035_PHY_ID 0x004dd072
2016-10-26 14:03:54 -02:00
# define AT803X_PHY_ID_MASK 0xffffffef
2014-06-18 11:01:42 +02:00
2012-10-14 19:07:16 +00:00
MODULE_DESCRIPTION ( " Atheros 803x PHY driver " ) ;
MODULE_AUTHOR ( " Matus Ujhelyi " ) ;
MODULE_LICENSE ( " GPL " ) ;
2014-06-18 11:01:43 +02:00
struct at803x_priv {
bool phy_reset : 1 ;
} ;
struct at803x_context {
u16 bmcr ;
u16 advertise ;
u16 control1000 ;
u16 int_enable ;
u16 smart_speed ;
u16 led_control ;
} ;
2016-01-15 01:55:22 +01:00
static int at803x_debug_reg_read ( struct phy_device * phydev , u16 reg )
{
int ret ;
ret = phy_write ( phydev , AT803X_DEBUG_ADDR , reg ) ;
if ( ret < 0 )
return ret ;
return phy_read ( phydev , AT803X_DEBUG_DATA ) ;
}
static int at803x_debug_reg_mask ( struct phy_device * phydev , u16 reg ,
u16 clear , u16 set )
{
u16 val ;
int ret ;
ret = at803x_debug_reg_read ( phydev , reg ) ;
if ( ret < 0 )
return ret ;
val = ret & 0xffff ;
val & = ~ clear ;
val | = set ;
return phy_write ( phydev , AT803X_DEBUG_DATA , val ) ;
}
static inline int at803x_enable_rx_delay ( struct phy_device * phydev )
{
return at803x_debug_reg_mask ( phydev , AT803X_DEBUG_REG_0 , 0 ,
AT803X_DEBUG_RX_CLK_DLY_EN ) ;
}
static inline int at803x_enable_tx_delay ( struct phy_device * phydev )
{
return at803x_debug_reg_mask ( phydev , AT803X_DEBUG_REG_5 , 0 ,
AT803X_DEBUG_TX_CLK_DLY_EN ) ;
}
2014-06-18 11:01:43 +02:00
/* save relevant PHY registers to private copy */
static void at803x_context_save ( struct phy_device * phydev ,
struct at803x_context * context )
{
context - > bmcr = phy_read ( phydev , MII_BMCR ) ;
context - > advertise = phy_read ( phydev , MII_ADVERTISE ) ;
context - > control1000 = phy_read ( phydev , MII_CTRL1000 ) ;
context - > int_enable = phy_read ( phydev , AT803X_INTR_ENABLE ) ;
context - > smart_speed = phy_read ( phydev , AT803X_SMART_SPEED ) ;
context - > led_control = phy_read ( phydev , AT803X_LED_CONTROL ) ;
}
/* restore relevant PHY registers from private copy */
static void at803x_context_restore ( struct phy_device * phydev ,
const struct at803x_context * context )
{
phy_write ( phydev , MII_BMCR , context - > bmcr ) ;
phy_write ( phydev , MII_ADVERTISE , context - > advertise ) ;
phy_write ( phydev , MII_CTRL1000 , context - > control1000 ) ;
phy_write ( phydev , AT803X_INTR_ENABLE , context - > int_enable ) ;
phy_write ( phydev , AT803X_SMART_SPEED , context - > smart_speed ) ;
phy_write ( phydev , AT803X_LED_CONTROL , context - > led_control ) ;
}
2013-06-03 20:10:05 +00:00
static int at803x_set_wol ( struct phy_device * phydev ,
struct ethtool_wolinfo * wol )
2012-10-14 19:07:16 +00:00
{
struct net_device * ndev = phydev - > attached_dev ;
const u8 * mac ;
2013-06-03 20:10:05 +00:00
int ret ;
u32 value ;
2012-10-14 19:07:16 +00:00
unsigned int i , offsets [ ] = {
AT803X_LOC_MAC_ADDR_32_47_OFFSET ,
AT803X_LOC_MAC_ADDR_16_31_OFFSET ,
AT803X_LOC_MAC_ADDR_0_15_OFFSET ,
} ;
if ( ! ndev )
2013-06-03 20:10:05 +00:00
return - ENODEV ;
2012-10-14 19:07:16 +00:00
2013-06-03 20:10:05 +00:00
if ( wol - > wolopts & WAKE_MAGIC ) {
mac = ( const u8 * ) ndev - > dev_addr ;
2012-10-14 19:07:16 +00:00
2013-06-03 20:10:05 +00:00
if ( ! is_valid_ether_addr ( mac ) )
2017-10-10 12:42:56 -05:00
return - EINVAL ;
2012-10-14 19:07:16 +00:00
2013-06-03 20:10:05 +00:00
for ( i = 0 ; i < 3 ; i + + ) {
phy_write ( phydev , AT803X_MMD_ACCESS_CONTROL ,
2012-10-14 19:07:16 +00:00
AT803X_DEVICE_ADDR ) ;
2013-06-03 20:10:05 +00:00
phy_write ( phydev , AT803X_MMD_ACCESS_CONTROL_DATA ,
2012-10-14 19:07:16 +00:00
offsets [ i ] ) ;
2013-06-03 20:10:05 +00:00
phy_write ( phydev , AT803X_MMD_ACCESS_CONTROL ,
2012-10-14 19:07:16 +00:00
AT803X_FUNC_DATA ) ;
2013-06-03 20:10:05 +00:00
phy_write ( phydev , AT803X_MMD_ACCESS_CONTROL_DATA ,
2012-10-14 19:07:16 +00:00
mac [ ( i * 2 ) + 1 ] | ( mac [ ( i * 2 ) ] < < 8 ) ) ;
2013-06-03 20:10:05 +00:00
}
value = phy_read ( phydev , AT803X_INTR_ENABLE ) ;
2016-01-15 01:55:24 +01:00
value | = AT803X_INTR_ENABLE_WOL ;
2013-06-03 20:10:05 +00:00
ret = phy_write ( phydev , AT803X_INTR_ENABLE , value ) ;
if ( ret )
return ret ;
value = phy_read ( phydev , AT803X_INTR_STATUS ) ;
} else {
value = phy_read ( phydev , AT803X_INTR_ENABLE ) ;
2016-01-15 01:55:24 +01:00
value & = ( ~ AT803X_INTR_ENABLE_WOL ) ;
2013-06-03 20:10:05 +00:00
ret = phy_write ( phydev , AT803X_INTR_ENABLE , value ) ;
if ( ret )
return ret ;
value = phy_read ( phydev , AT803X_INTR_STATUS ) ;
2012-10-14 19:07:16 +00:00
}
2013-06-03 20:10:05 +00:00
return ret ;
}
static void at803x_get_wol ( struct phy_device * phydev ,
struct ethtool_wolinfo * wol )
{
u32 value ;
wol - > supported = WAKE_MAGIC ;
wol - > wolopts = 0 ;
value = phy_read ( phydev , AT803X_INTR_ENABLE ) ;
2016-01-15 01:55:24 +01:00
if ( value & AT803X_INTR_ENABLE_WOL )
2013-06-03 20:10:05 +00:00
wol - > wolopts | = WAKE_MAGIC ;
2012-10-14 19:07:16 +00:00
}
2013-09-21 16:53:02 +02:00
static int at803x_suspend ( struct phy_device * phydev )
{
int value ;
int wol_enabled ;
value = phy_read ( phydev , AT803X_INTR_ENABLE ) ;
2016-01-15 01:55:24 +01:00
wol_enabled = value & AT803X_INTR_ENABLE_WOL ;
2013-09-21 16:53:02 +02:00
if ( wol_enabled )
2018-01-02 10:58:58 +00:00
value = BMCR_ISOLATE ;
2013-09-21 16:53:02 +02:00
else
2018-01-02 10:58:58 +00:00
value = BMCR_PDOWN ;
2013-09-21 16:53:02 +02:00
2018-01-02 10:58:58 +00:00
phy_modify ( phydev , MII_BMCR , 0 , value ) ;
2013-09-21 16:53:02 +02:00
return 0 ;
}
static int at803x_resume ( struct phy_device * phydev )
{
2018-01-05 16:07:10 +00:00
return phy_modify ( phydev , MII_BMCR , BMCR_PDOWN | BMCR_ISOLATE , 0 ) ;
2013-09-21 16:53:02 +02:00
}
2014-06-18 11:01:43 +02:00
static int at803x_probe ( struct phy_device * phydev )
{
2016-01-06 20:11:16 +01:00
struct device * dev = & phydev - > mdio . dev ;
2014-06-18 11:01:43 +02:00
struct at803x_priv * priv ;
2014-06-22 12:32:51 +02:00
priv = devm_kzalloc ( dev , sizeof ( * priv ) , GFP_KERNEL ) ;
2014-06-18 11:01:43 +02:00
if ( ! priv )
return - ENOMEM ;
phydev - > priv = priv ;
return 0 ;
}
2012-10-14 19:07:16 +00:00
static int at803x_config_init ( struct phy_device * phydev )
{
2013-06-03 20:10:06 +00:00
int ret ;
2012-10-14 19:07:16 +00:00
2014-04-16 17:19:13 +02:00
ret = genphy_config_init ( phydev ) ;
if ( ret < 0 )
return ret ;
2012-10-14 19:07:16 +00:00
2016-01-15 01:55:22 +01:00
if ( phydev - > interface = = PHY_INTERFACE_MODE_RGMII_RXID | |
phydev - > interface = = PHY_INTERFACE_MODE_RGMII_ID ) {
ret = at803x_enable_rx_delay ( phydev ) ;
if ( ret < 0 )
2013-06-03 20:10:06 +00:00
return ret ;
2016-01-15 01:55:22 +01:00
}
if ( phydev - > interface = = PHY_INTERFACE_MODE_RGMII_TXID | |
phydev - > interface = = PHY_INTERFACE_MODE_RGMII_ID ) {
ret = at803x_enable_tx_delay ( phydev ) ;
if ( ret < 0 )
2013-06-03 20:10:06 +00:00
return ret ;
}
2012-10-14 19:07:16 +00:00
return 0 ;
}
2014-03-28 15:39:41 +08:00
static int at803x_ack_interrupt ( struct phy_device * phydev )
{
int err ;
2016-01-15 01:55:23 +01:00
err = phy_read ( phydev , AT803X_INTR_STATUS ) ;
2014-03-28 15:39:41 +08:00
return ( err < 0 ) ? err : 0 ;
}
static int at803x_config_intr ( struct phy_device * phydev )
{
int err ;
int value ;
2016-01-15 01:55:23 +01:00
value = phy_read ( phydev , AT803X_INTR_ENABLE ) ;
2014-03-28 15:39:41 +08:00
2016-01-15 01:55:24 +01:00
if ( phydev - > interrupts = = PHY_INTERRUPT_ENABLED ) {
value | = AT803X_INTR_ENABLE_AUTONEG_ERR ;
value | = AT803X_INTR_ENABLE_SPEED_CHANGED ;
value | = AT803X_INTR_ENABLE_DUPLEX_CHANGED ;
value | = AT803X_INTR_ENABLE_LINK_FAIL ;
value | = AT803X_INTR_ENABLE_LINK_SUCCESS ;
err = phy_write ( phydev , AT803X_INTR_ENABLE , value ) ;
}
2014-03-28 15:39:41 +08:00
else
2016-01-15 01:55:23 +01:00
err = phy_write ( phydev , AT803X_INTR_ENABLE , 0 ) ;
2014-03-28 15:39:41 +08:00
return err ;
}
2014-06-18 11:01:43 +02:00
static void at803x_link_change_notify ( struct phy_device * phydev )
{
struct at803x_priv * priv = phydev - > priv ;
/*
* Conduct a hardware reset for AT8030 every time a link loss is
* signalled . This is necessary to circumvent a hardware bug that
* occurs when the cable is unplugged while TX packets are pending
* in the FIFO . In such cases , the FIFO enters an error mode it
* cannot recover from by software .
*/
2016-04-26 12:44:18 -05:00
if ( phydev - > state = = PHY_NOLINK ) {
2017-12-04 13:35:05 +01:00
if ( phydev - > mdio . reset & & ! priv - > phy_reset ) {
2016-04-26 12:44:18 -05:00
struct at803x_context context ;
at803x_context_save ( phydev , & context ) ;
2017-12-04 13:35:05 +01:00
phy_device_reset ( phydev , 1 ) ;
2016-04-26 12:44:18 -05:00
msleep ( 1 ) ;
2017-12-04 13:35:05 +01:00
phy_device_reset ( phydev , 0 ) ;
2016-04-26 12:44:18 -05:00
msleep ( 1 ) ;
at803x_context_restore ( phydev , & context ) ;
phydev_dbg ( phydev , " %s(): phy was reset \n " ,
__func__ ) ;
priv - > phy_reset = true ;
2014-06-18 11:01:43 +02:00
}
2016-04-26 12:44:18 -05:00
} else {
priv - > phy_reset = false ;
2014-06-18 11:01:43 +02:00
}
}
2016-10-24 12:40:54 +02:00
static int at803x_aneg_done ( struct phy_device * phydev )
{
int ccr ;
int aneg_done = genphy_aneg_done ( phydev ) ;
if ( aneg_done ! = BMSR_ANEGCOMPLETE )
return aneg_done ;
/*
* in SGMII mode , if copper side autoneg is successful ,
* also check SGMII side autoneg result
*/
ccr = phy_read ( phydev , AT803X_REG_CHIP_CONFIG ) ;
if ( ( ccr & AT803X_MODE_CFG_MASK ) ! = AT803X_MODE_CFG_SGMII )
return aneg_done ;
/* switch to SGMII/fiber page */
phy_write ( phydev , AT803X_REG_CHIP_CONFIG , ccr & ~ AT803X_BT_BX_REG_SEL ) ;
/* check if the SGMII link is OK. */
if ( ! ( phy_read ( phydev , AT803X_PSSR ) & AT803X_PSSR_MR_AN_COMPLETE ) ) {
pr_warn ( " 803x_aneg_done: SGMII link is not ok \n " ) ;
aneg_done = 0 ;
}
/* switch back to copper page */
phy_write ( phydev , AT803X_REG_CHIP_CONFIG , ccr | AT803X_BT_BX_REG_SEL ) ;
return aneg_done ;
}
2013-06-03 20:10:04 +00:00
static struct phy_driver at803x_driver [ ] = {
{
/* ATHEROS 8035 */
2014-06-18 11:01:43 +02:00
. phy_id = ATH8035_PHY_ID ,
. name = " Atheros 8035 ethernet " ,
2016-10-26 14:03:54 -02:00
. phy_id_mask = AT803X_PHY_ID_MASK ,
2014-06-18 11:01:43 +02:00
. probe = at803x_probe ,
. config_init = at803x_config_init ,
. set_wol = at803x_set_wol ,
. get_wol = at803x_get_wol ,
. suspend = at803x_suspend ,
. resume = at803x_resume ,
. features = PHY_GBIT_FEATURES ,
. flags = PHY_HAS_INTERRUPT ,
2015-11-12 17:40:20 +00:00
. ack_interrupt = at803x_ack_interrupt ,
. config_intr = at803x_config_intr ,
2013-06-03 20:10:04 +00:00
} , {
/* ATHEROS 8030 */
2014-06-18 11:01:43 +02:00
. phy_id = ATH8030_PHY_ID ,
. name = " Atheros 8030 ethernet " ,
2016-10-26 14:03:54 -02:00
. phy_id_mask = AT803X_PHY_ID_MASK ,
2014-06-18 11:01:43 +02:00
. probe = at803x_probe ,
. config_init = at803x_config_init ,
. link_change_notify = at803x_link_change_notify ,
. set_wol = at803x_set_wol ,
. get_wol = at803x_get_wol ,
. suspend = at803x_suspend ,
. resume = at803x_resume ,
2016-01-15 01:55:21 +01:00
. features = PHY_BASIC_FEATURES ,
2014-06-18 11:01:43 +02:00
. flags = PHY_HAS_INTERRUPT ,
2015-11-12 17:40:20 +00:00
. ack_interrupt = at803x_ack_interrupt ,
. config_intr = at803x_config_intr ,
2013-06-03 20:10:07 +00:00
} , {
/* ATHEROS 8031 */
2014-06-18 11:01:43 +02:00
. phy_id = ATH8031_PHY_ID ,
. name = " Atheros 8031 ethernet " ,
2016-10-26 14:03:54 -02:00
. phy_id_mask = AT803X_PHY_ID_MASK ,
2014-06-18 11:01:43 +02:00
. probe = at803x_probe ,
. config_init = at803x_config_init ,
. set_wol = at803x_set_wol ,
. get_wol = at803x_get_wol ,
. suspend = at803x_suspend ,
. resume = at803x_resume ,
. features = PHY_GBIT_FEATURES ,
. flags = PHY_HAS_INTERRUPT ,
2016-10-24 12:40:54 +02:00
. aneg_done = at803x_aneg_done ,
2014-06-18 11:01:43 +02:00
. ack_interrupt = & at803x_ack_interrupt ,
. config_intr = & at803x_config_intr ,
2013-06-03 20:10:04 +00:00
} } ;
2012-10-14 19:07:16 +00:00
2014-11-11 19:45:59 +01:00
module_phy_driver ( at803x_driver ) ;
2012-10-14 19:07:16 +00:00
static struct mdio_device_id __maybe_unused atheros_tbl [ ] = {
2016-10-26 14:03:54 -02:00
{ ATH8030_PHY_ID , AT803X_PHY_ID_MASK } ,
{ ATH8031_PHY_ID , AT803X_PHY_ID_MASK } ,
{ ATH8035_PHY_ID , AT803X_PHY_ID_MASK } ,
2012-10-14 19:07:16 +00:00
{ }
} ;
MODULE_DEVICE_TABLE ( mdio , atheros_tbl ) ;