2019-01-21 19:05:50 +01:00
// SPDX-License-Identifier: GPL-2.0
2015-07-31 16:58:42 +08:00
/*
* Driver for Aquantia PHY
*
* Author : Shaohui Xie < Shaohui . Xie @ freescale . com >
*
* Copyright 2015 Freescale Semiconductor , Inc .
*/
# include <linux/kernel.h>
# include <linux/module.h>
# include <linux/delay.h>
2019-03-19 23:05:50 +01:00
# include <linux/bitfield.h>
2015-07-31 16:58:42 +08:00
# include <linux/phy.h>
2019-02-25 19:56:38 +01:00
# include "aquantia.h"
2015-07-31 16:58:42 +08:00
# define PHY_ID_AQ1202 0x03a1b445
# define PHY_ID_AQ2104 0x03a1b460
# define PHY_ID_AQR105 0x03a1b4a2
2016-10-20 16:30:31 +08:00
# define PHY_ID_AQR106 0x03a1b4d0
# define PHY_ID_AQR107 0x03a1b4e0
2019-02-08 22:12:23 +01:00
# define PHY_ID_AQCS109 0x03a1b5c2
2015-07-31 16:58:42 +08:00
# define PHY_ID_AQR405 0x03a1b4b0
2019-03-19 23:05:50 +01:00
# define MDIO_PHYXS_VEND_IF_STATUS 0xe812
# define MDIO_PHYXS_VEND_IF_STATUS_TYPE_MASK GENMASK(7, 3)
# define MDIO_PHYXS_VEND_IF_STATUS_TYPE_KR 0
# define MDIO_PHYXS_VEND_IF_STATUS_TYPE_XFI 2
2019-05-23 20:09:08 +02:00
# define MDIO_PHYXS_VEND_IF_STATUS_TYPE_USXGMII 3
2019-03-19 23:05:50 +01:00
# define MDIO_PHYXS_VEND_IF_STATUS_TYPE_SGMII 6
# define MDIO_PHYXS_VEND_IF_STATUS_TYPE_OCSGMII 10
2019-02-22 23:49:54 +01:00
# define MDIO_AN_VEND_PROV 0xc400
# define MDIO_AN_VEND_PROV_1000BASET_FULL BIT(15)
# define MDIO_AN_VEND_PROV_1000BASET_HALF BIT(14)
2019-03-21 21:08:35 +01:00
# define MDIO_AN_VEND_PROV_DOWNSHIFT_EN BIT(4)
# define MDIO_AN_VEND_PROV_DOWNSHIFT_MASK GENMASK(3, 0)
# define MDIO_AN_VEND_PROV_DOWNSHIFT_DFLT 4
2019-02-22 23:49:54 +01:00
2019-02-03 21:19:06 +01:00
# define MDIO_AN_TX_VEND_STATUS1 0xc800
2019-03-21 21:08:35 +01:00
# define MDIO_AN_TX_VEND_STATUS1_RATE_MASK GENMASK(3, 1)
# define MDIO_AN_TX_VEND_STATUS1_10BASET 0
# define MDIO_AN_TX_VEND_STATUS1_100BASETX 1
# define MDIO_AN_TX_VEND_STATUS1_1000BASET 2
# define MDIO_AN_TX_VEND_STATUS1_10GBASET 3
# define MDIO_AN_TX_VEND_STATUS1_2500BASET 4
# define MDIO_AN_TX_VEND_STATUS1_5000BASET 5
2019-02-03 21:19:06 +01:00
# define MDIO_AN_TX_VEND_STATUS1_FULL_DUPLEX BIT(0)
2019-03-21 21:08:35 +01:00
# define MDIO_AN_TX_VEND_INT_STATUS1 0xcc00
# define MDIO_AN_TX_VEND_INT_STATUS1_DOWNSHIFT BIT(1)
2019-02-03 21:19:06 +01:00
# define MDIO_AN_TX_VEND_INT_STATUS2 0xcc01
# define MDIO_AN_TX_VEND_INT_MASK2 0xd401
# define MDIO_AN_TX_VEND_INT_MASK2_LINK BIT(0)
2019-02-22 23:52:32 +01:00
# define MDIO_AN_RX_LP_STAT1 0xe820
# define MDIO_AN_RX_LP_STAT1_1000BASET_FULL BIT(15)
# define MDIO_AN_RX_LP_STAT1_1000BASET_HALF BIT(14)
2019-03-24 11:04:21 +01:00
# define MDIO_AN_RX_LP_STAT1_SHORT_REACH BIT(13)
# define MDIO_AN_RX_LP_STAT1_AQRATE_DOWNSHIFT BIT(12)
# define MDIO_AN_RX_LP_STAT1_AQ_PHY BIT(2)
# define MDIO_AN_RX_LP_STAT4 0xe823
# define MDIO_AN_RX_LP_STAT4_FW_MAJOR GENMASK(15, 8)
# define MDIO_AN_RX_LP_STAT4_FW_MINOR GENMASK(7, 0)
# define MDIO_AN_RX_VEND_STAT3 0xe832
# define MDIO_AN_RX_VEND_STAT3_AFR BIT(0)
2019-02-22 23:52:32 +01:00
2019-03-31 17:42:24 +02:00
/* MDIO_MMD_C22EXT */
# define MDIO_C22EXT_STAT_SGMII_RX_GOOD_FRAMES 0xd292
# define MDIO_C22EXT_STAT_SGMII_RX_BAD_FRAMES 0xd294
# define MDIO_C22EXT_STAT_SGMII_RX_FALSE_CARRIER 0xd297
# define MDIO_C22EXT_STAT_SGMII_TX_GOOD_FRAMES 0xd313
# define MDIO_C22EXT_STAT_SGMII_TX_BAD_FRAMES 0xd315
# define MDIO_C22EXT_STAT_SGMII_TX_FALSE_CARRIER 0xd317
# define MDIO_C22EXT_STAT_SGMII_TX_COLLISIONS 0xd318
# define MDIO_C22EXT_STAT_SGMII_TX_LINE_COLLISIONS 0xd319
# define MDIO_C22EXT_STAT_SGMII_TX_FRAME_ALIGN_ERR 0xd31a
# define MDIO_C22EXT_STAT_SGMII_TX_RUNT_FRAMES 0xd31b
2019-02-03 21:19:06 +01:00
/* Vendor specific 1, MDIO_MMD_VEND1 */
2019-03-24 11:08:13 +01:00
# define VEND1_GLOBAL_FW_ID 0x0020
# define VEND1_GLOBAL_FW_ID_MAJOR GENMASK(15, 8)
# define VEND1_GLOBAL_FW_ID_MINOR GENMASK(7, 0)
# define VEND1_GLOBAL_RSVD_STAT1 0xc885
# define VEND1_GLOBAL_RSVD_STAT1_FW_BUILD_ID GENMASK(7, 4)
# define VEND1_GLOBAL_RSVD_STAT1_PROV_ID GENMASK(3, 0)
2019-03-24 11:09:41 +01:00
# define VEND1_GLOBAL_RSVD_STAT9 0xc88d
# define VEND1_GLOBAL_RSVD_STAT9_MODE GENMASK(7, 0)
# define VEND1_GLOBAL_RSVD_STAT9_1000BT2 0x23
2019-02-03 21:19:06 +01:00
# define VEND1_GLOBAL_INT_STD_STATUS 0xfc00
# define VEND1_GLOBAL_INT_VEND_STATUS 0xfc01
# define VEND1_GLOBAL_INT_STD_MASK 0xff00
# define VEND1_GLOBAL_INT_STD_MASK_PMA1 BIT(15)
# define VEND1_GLOBAL_INT_STD_MASK_PMA2 BIT(14)
# define VEND1_GLOBAL_INT_STD_MASK_PCS1 BIT(13)
# define VEND1_GLOBAL_INT_STD_MASK_PCS2 BIT(12)
# define VEND1_GLOBAL_INT_STD_MASK_PCS3 BIT(11)
# define VEND1_GLOBAL_INT_STD_MASK_PHY_XS1 BIT(10)
# define VEND1_GLOBAL_INT_STD_MASK_PHY_XS2 BIT(9)
# define VEND1_GLOBAL_INT_STD_MASK_AN1 BIT(8)
# define VEND1_GLOBAL_INT_STD_MASK_AN2 BIT(7)
# define VEND1_GLOBAL_INT_STD_MASK_GBE BIT(6)
# define VEND1_GLOBAL_INT_STD_MASK_ALL BIT(0)
# define VEND1_GLOBAL_INT_VEND_MASK 0xff01
# define VEND1_GLOBAL_INT_VEND_MASK_PMA BIT(15)
# define VEND1_GLOBAL_INT_VEND_MASK_PCS BIT(14)
# define VEND1_GLOBAL_INT_VEND_MASK_PHY_XS BIT(13)
# define VEND1_GLOBAL_INT_VEND_MASK_AN BIT(12)
# define VEND1_GLOBAL_INT_VEND_MASK_GBE BIT(11)
# define VEND1_GLOBAL_INT_VEND_MASK_GLOBAL1 BIT(2)
# define VEND1_GLOBAL_INT_VEND_MASK_GLOBAL2 BIT(1)
# define VEND1_GLOBAL_INT_VEND_MASK_GLOBAL3 BIT(0)
2019-03-31 17:42:24 +02:00
struct aqr107_hw_stat {
const char * name ;
int reg ;
int size ;
} ;
# define SGMII_STAT(n, r, s) { n, MDIO_C22EXT_STAT_SGMII_ ## r, s }
static const struct aqr107_hw_stat aqr107_hw_stats [ ] = {
SGMII_STAT ( " sgmii_rx_good_frames " , RX_GOOD_FRAMES , 26 ) ,
SGMII_STAT ( " sgmii_rx_bad_frames " , RX_BAD_FRAMES , 26 ) ,
SGMII_STAT ( " sgmii_rx_false_carrier_events " , RX_FALSE_CARRIER , 8 ) ,
SGMII_STAT ( " sgmii_tx_good_frames " , TX_GOOD_FRAMES , 26 ) ,
SGMII_STAT ( " sgmii_tx_bad_frames " , TX_BAD_FRAMES , 26 ) ,
SGMII_STAT ( " sgmii_tx_false_carrier_events " , TX_FALSE_CARRIER , 8 ) ,
SGMII_STAT ( " sgmii_tx_collisions " , TX_COLLISIONS , 8 ) ,
SGMII_STAT ( " sgmii_tx_line_collisions " , TX_LINE_COLLISIONS , 8 ) ,
SGMII_STAT ( " sgmii_tx_frame_alignment_err " , TX_FRAME_ALIGN_ERR , 16 ) ,
SGMII_STAT ( " sgmii_tx_runt_frames " , TX_RUNT_FRAMES , 22 ) ,
} ;
# define AQR107_SGMII_STAT_SZ ARRAY_SIZE(aqr107_hw_stats)
struct aqr107_priv {
u64 sgmii_stats [ AQR107_SGMII_STAT_SZ ] ;
} ;
static int aqr107_get_sset_count ( struct phy_device * phydev )
{
return AQR107_SGMII_STAT_SZ ;
}
static void aqr107_get_strings ( struct phy_device * phydev , u8 * data )
{
int i ;
for ( i = 0 ; i < AQR107_SGMII_STAT_SZ ; i + + )
strscpy ( data + i * ETH_GSTRING_LEN , aqr107_hw_stats [ i ] . name ,
ETH_GSTRING_LEN ) ;
}
static u64 aqr107_get_stat ( struct phy_device * phydev , int index )
{
const struct aqr107_hw_stat * stat = aqr107_hw_stats + index ;
int len_l = min ( stat - > size , 16 ) ;
int len_h = stat - > size - len_l ;
u64 ret ;
int val ;
val = phy_read_mmd ( phydev , MDIO_MMD_C22EXT , stat - > reg ) ;
if ( val < 0 )
return U64_MAX ;
ret = val & GENMASK ( len_l - 1 , 0 ) ;
if ( len_h ) {
val = phy_read_mmd ( phydev , MDIO_MMD_C22EXT , stat - > reg + 1 ) ;
if ( val < 0 )
return U64_MAX ;
ret + = ( val & GENMASK ( len_h - 1 , 0 ) ) < < 16 ;
}
return ret ;
}
static void aqr107_get_stats ( struct phy_device * phydev ,
struct ethtool_stats * stats , u64 * data )
{
struct aqr107_priv * priv = phydev - > priv ;
u64 val ;
int i ;
for ( i = 0 ; i < AQR107_SGMII_STAT_SZ ; i + + ) {
val = aqr107_get_stat ( phydev , i ) ;
if ( val = = U64_MAX )
phydev_err ( phydev , " Reading HW Statistics failed for %s \n " ,
aqr107_hw_stats [ i ] . name ) ;
else
priv - > sgmii_stats [ i ] + = val ;
data [ i ] = priv - > sgmii_stats [ i ] ;
}
}
2019-02-03 21:16:18 +01:00
static int aqr_config_aneg ( struct phy_device * phydev )
2015-07-31 16:58:42 +08:00
{
2019-02-22 23:49:54 +01:00
bool changed = false ;
u16 reg ;
int ret ;
if ( phydev - > autoneg = = AUTONEG_DISABLE )
return genphy_c45_pma_setup_forced ( phydev ) ;
ret = genphy_c45_an_config_aneg ( phydev ) ;
if ( ret < 0 )
return ret ;
if ( ret > 0 )
changed = true ;
/* Clause 45 has no standardized support for 1000BaseT, therefore
* use vendor registers for this mode .
*/
reg = 0 ;
if ( linkmode_test_bit ( ETHTOOL_LINK_MODE_1000baseT_Full_BIT ,
phydev - > advertising ) )
reg | = MDIO_AN_VEND_PROV_1000BASET_FULL ;
if ( linkmode_test_bit ( ETHTOOL_LINK_MODE_1000baseT_Half_BIT ,
phydev - > advertising ) )
reg | = MDIO_AN_VEND_PROV_1000BASET_HALF ;
ret = phy_modify_mmd_changed ( phydev , MDIO_MMD_AN , MDIO_AN_VEND_PROV ,
MDIO_AN_VEND_PROV_1000BASET_HALF |
MDIO_AN_VEND_PROV_1000BASET_FULL , reg ) ;
if ( ret < 0 )
return ret ;
if ( ret > 0 )
changed = true ;
2015-07-31 16:58:42 +08:00
2019-02-22 23:49:54 +01:00
return genphy_c45_check_and_restart_aneg ( phydev , changed ) ;
2015-07-31 16:58:42 +08:00
}
2019-02-03 21:16:18 +01:00
static int aqr_config_intr ( struct phy_device * phydev )
2015-08-21 15:29:29 +08:00
{
2019-03-23 14:35:20 +01:00
bool en = phydev - > interrupts = = PHY_INTERRUPT_ENABLED ;
2015-08-21 15:29:29 +08:00
int err ;
2019-03-23 14:35:20 +01:00
err = phy_write_mmd ( phydev , MDIO_MMD_AN , MDIO_AN_TX_VEND_INT_MASK2 ,
en ? MDIO_AN_TX_VEND_INT_MASK2_LINK : 0 ) ;
if ( err < 0 )
return err ;
err = phy_write_mmd ( phydev , MDIO_MMD_VEND1 , VEND1_GLOBAL_INT_STD_MASK ,
en ? VEND1_GLOBAL_INT_STD_MASK_ALL : 0 ) ;
if ( err < 0 )
return err ;
return phy_write_mmd ( phydev , MDIO_MMD_VEND1 , VEND1_GLOBAL_INT_VEND_MASK ,
en ? VEND1_GLOBAL_INT_VEND_MASK_GLOBAL3 |
VEND1_GLOBAL_INT_VEND_MASK_AN : 0 ) ;
2015-08-21 15:29:29 +08:00
}
2019-02-03 21:16:18 +01:00
static int aqr_ack_interrupt ( struct phy_device * phydev )
2015-08-21 15:29:29 +08:00
{
int reg ;
2019-02-03 21:19:06 +01:00
reg = phy_read_mmd ( phydev , MDIO_MMD_AN ,
MDIO_AN_TX_VEND_INT_STATUS2 ) ;
2015-08-21 15:29:29 +08:00
return ( reg < 0 ) ? reg : 0 ;
}
2019-02-03 21:16:18 +01:00
static int aqr_read_status ( struct phy_device * phydev )
2015-07-31 16:58:42 +08:00
{
2019-02-22 23:52:32 +01:00
int val ;
if ( phydev - > autoneg = = AUTONEG_ENABLE ) {
val = phy_read_mmd ( phydev , MDIO_MMD_AN , MDIO_AN_RX_LP_STAT1 ) ;
if ( val < 0 )
return val ;
linkmode_mod_bit ( ETHTOOL_LINK_MODE_1000baseT_Full_BIT ,
phydev - > lp_advertising ,
val & MDIO_AN_RX_LP_STAT1_1000BASET_FULL ) ;
linkmode_mod_bit ( ETHTOOL_LINK_MODE_1000baseT_Half_BIT ,
phydev - > lp_advertising ,
val & MDIO_AN_RX_LP_STAT1_1000BASET_HALF ) ;
2015-07-31 16:58:42 +08:00
}
2019-02-22 23:52:32 +01:00
return genphy_c45_read_status ( phydev ) ;
2015-07-31 16:58:42 +08:00
}
2019-03-21 21:08:35 +01:00
static int aqr107_read_downshift_event ( struct phy_device * phydev )
{
int val ;
val = phy_read_mmd ( phydev , MDIO_MMD_AN , MDIO_AN_TX_VEND_INT_STATUS1 ) ;
if ( val < 0 )
return val ;
return ! ! ( val & MDIO_AN_TX_VEND_INT_STATUS1_DOWNSHIFT ) ;
}
static int aqr107_read_rate ( struct phy_device * phydev )
{
int val ;
val = phy_read_mmd ( phydev , MDIO_MMD_AN , MDIO_AN_TX_VEND_STATUS1 ) ;
if ( val < 0 )
return val ;
switch ( FIELD_GET ( MDIO_AN_TX_VEND_STATUS1_RATE_MASK , val ) ) {
case MDIO_AN_TX_VEND_STATUS1_10BASET :
phydev - > speed = SPEED_10 ;
break ;
case MDIO_AN_TX_VEND_STATUS1_100BASETX :
phydev - > speed = SPEED_100 ;
break ;
case MDIO_AN_TX_VEND_STATUS1_1000BASET :
phydev - > speed = SPEED_1000 ;
break ;
case MDIO_AN_TX_VEND_STATUS1_2500BASET :
phydev - > speed = SPEED_2500 ;
break ;
case MDIO_AN_TX_VEND_STATUS1_5000BASET :
phydev - > speed = SPEED_5000 ;
break ;
case MDIO_AN_TX_VEND_STATUS1_10GBASET :
phydev - > speed = SPEED_10000 ;
break ;
default :
phydev - > speed = SPEED_UNKNOWN ;
break ;
}
if ( val & MDIO_AN_TX_VEND_STATUS1_FULL_DUPLEX )
phydev - > duplex = DUPLEX_FULL ;
else
phydev - > duplex = DUPLEX_HALF ;
return 0 ;
}
2019-03-19 23:05:50 +01:00
static int aqr107_read_status ( struct phy_device * phydev )
{
int val , ret ;
ret = aqr_read_status ( phydev ) ;
if ( ret )
return ret ;
2019-03-21 21:08:35 +01:00
if ( ! phydev - > link | | phydev - > autoneg = = AUTONEG_DISABLE )
2019-03-19 23:05:50 +01:00
return 0 ;
val = phy_read_mmd ( phydev , MDIO_MMD_PHYXS , MDIO_PHYXS_VEND_IF_STATUS ) ;
if ( val < 0 )
return val ;
switch ( FIELD_GET ( MDIO_PHYXS_VEND_IF_STATUS_TYPE_MASK , val ) ) {
case MDIO_PHYXS_VEND_IF_STATUS_TYPE_KR :
case MDIO_PHYXS_VEND_IF_STATUS_TYPE_XFI :
phydev - > interface = PHY_INTERFACE_MODE_10GKR ;
break ;
2019-05-23 20:09:08 +02:00
case MDIO_PHYXS_VEND_IF_STATUS_TYPE_USXGMII :
phydev - > interface = PHY_INTERFACE_MODE_USXGMII ;
break ;
2019-03-19 23:05:50 +01:00
case MDIO_PHYXS_VEND_IF_STATUS_TYPE_SGMII :
phydev - > interface = PHY_INTERFACE_MODE_SGMII ;
break ;
case MDIO_PHYXS_VEND_IF_STATUS_TYPE_OCSGMII :
phydev - > interface = PHY_INTERFACE_MODE_2500BASEX ;
break ;
default :
phydev - > interface = PHY_INTERFACE_MODE_NA ;
break ;
}
2019-03-21 21:08:35 +01:00
val = aqr107_read_downshift_event ( phydev ) ;
if ( val < = 0 )
return val ;
phydev_warn ( phydev , " Downshift occurred! Cabling may be defective. \n " ) ;
/* Read downshifted rate from vendor register */
return aqr107_read_rate ( phydev ) ;
}
static int aqr107_get_downshift ( struct phy_device * phydev , u8 * data )
{
int val , cnt , enable ;
val = phy_read_mmd ( phydev , MDIO_MMD_AN , MDIO_AN_VEND_PROV ) ;
if ( val < 0 )
return val ;
enable = FIELD_GET ( MDIO_AN_VEND_PROV_DOWNSHIFT_EN , val ) ;
cnt = FIELD_GET ( MDIO_AN_VEND_PROV_DOWNSHIFT_MASK , val ) ;
* data = enable & & cnt ? cnt : DOWNSHIFT_DEV_DISABLE ;
2019-03-19 23:05:50 +01:00
return 0 ;
}
2019-03-21 21:08:35 +01:00
static int aqr107_set_downshift ( struct phy_device * phydev , u8 cnt )
{
int val = 0 ;
if ( ! FIELD_FIT ( MDIO_AN_VEND_PROV_DOWNSHIFT_MASK , cnt ) )
return - E2BIG ;
if ( cnt ! = DOWNSHIFT_DEV_DISABLE ) {
val = MDIO_AN_VEND_PROV_DOWNSHIFT_EN ;
val | = FIELD_PREP ( MDIO_AN_VEND_PROV_DOWNSHIFT_MASK , cnt ) ;
}
return phy_modify_mmd ( phydev , MDIO_MMD_AN , MDIO_AN_VEND_PROV ,
MDIO_AN_VEND_PROV_DOWNSHIFT_EN |
MDIO_AN_VEND_PROV_DOWNSHIFT_MASK , val ) ;
}
static int aqr107_get_tunable ( struct phy_device * phydev ,
struct ethtool_tunable * tuna , void * data )
{
switch ( tuna - > id ) {
case ETHTOOL_PHY_DOWNSHIFT :
return aqr107_get_downshift ( phydev , data ) ;
default :
return - EOPNOTSUPP ;
}
}
static int aqr107_set_tunable ( struct phy_device * phydev ,
struct ethtool_tunable * tuna , const void * data )
{
switch ( tuna - > id ) {
case ETHTOOL_PHY_DOWNSHIFT :
return aqr107_set_downshift ( phydev , * ( const u8 * ) data ) ;
default :
return - EOPNOTSUPP ;
}
}
2019-03-24 11:08:13 +01:00
/* If we configure settings whilst firmware is still initializing the chip,
* then these settings may be overwritten . Therefore make sure chip
* initialization has completed . Use presence of the firmware ID as
* indicator for initialization having completed .
* The chip also provides a " reset completed " bit , but it ' s cleared after
* read . Therefore function would time out if called again .
*/
static int aqr107_wait_reset_complete ( struct phy_device * phydev )
{
int val , retries = 100 ;
do {
val = phy_read_mmd ( phydev , MDIO_MMD_VEND1 , VEND1_GLOBAL_FW_ID ) ;
if ( val < 0 )
return val ;
msleep ( 20 ) ;
} while ( ! val & & - - retries ) ;
return val ? 0 : - ETIMEDOUT ;
}
static void aqr107_chip_info ( struct phy_device * phydev )
{
u8 fw_major , fw_minor , build_id , prov_id ;
int val ;
val = phy_read_mmd ( phydev , MDIO_MMD_VEND1 , VEND1_GLOBAL_FW_ID ) ;
if ( val < 0 )
return ;
fw_major = FIELD_GET ( VEND1_GLOBAL_FW_ID_MAJOR , val ) ;
fw_minor = FIELD_GET ( VEND1_GLOBAL_FW_ID_MINOR , val ) ;
val = phy_read_mmd ( phydev , MDIO_MMD_VEND1 , VEND1_GLOBAL_RSVD_STAT1 ) ;
if ( val < 0 )
return ;
build_id = FIELD_GET ( VEND1_GLOBAL_RSVD_STAT1_FW_BUILD_ID , val ) ;
prov_id = FIELD_GET ( VEND1_GLOBAL_RSVD_STAT1_PROV_ID , val ) ;
phydev_dbg ( phydev , " FW %u.%u, Build %u, Provisioning %u \n " ,
fw_major , fw_minor , build_id , prov_id ) ;
}
2019-03-19 23:04:38 +01:00
static int aqr107_config_init ( struct phy_device * phydev )
{
2019-03-24 11:08:13 +01:00
int ret ;
2019-03-19 23:04:38 +01:00
/* Check that the PHY interface type is compatible */
if ( phydev - > interface ! = PHY_INTERFACE_MODE_SGMII & &
phydev - > interface ! = PHY_INTERFACE_MODE_2500BASEX & &
2019-05-15 15:07:44 +00:00
phydev - > interface ! = PHY_INTERFACE_MODE_XGMII & &
2019-05-23 20:09:08 +02:00
phydev - > interface ! = PHY_INTERFACE_MODE_USXGMII & &
2019-03-19 23:04:38 +01:00
phydev - > interface ! = PHY_INTERFACE_MODE_10GKR )
return - ENODEV ;
2019-05-23 20:09:08 +02:00
WARN ( phydev - > interface = = PHY_INTERFACE_MODE_XGMII ,
" Your devicetree is out of date, please update it. The AQR107 family doesn't support XGMII, maybe you mean USXGMII. \n " ) ;
2019-03-24 11:08:13 +01:00
ret = aqr107_wait_reset_complete ( phydev ) ;
if ( ! ret )
aqr107_chip_info ( phydev ) ;
2019-03-21 21:08:35 +01:00
/* ensure that a latched downshift event is cleared */
aqr107_read_downshift_event ( phydev ) ;
return aqr107_set_downshift ( phydev , MDIO_AN_VEND_PROV_DOWNSHIFT_DFLT ) ;
2019-03-19 23:04:38 +01:00
}
2019-02-22 23:48:14 +01:00
static int aqcs109_config_init ( struct phy_device * phydev )
{
2019-03-21 21:08:35 +01:00
int ret ;
2019-03-19 23:04:38 +01:00
/* Check that the PHY interface type is compatible */
if ( phydev - > interface ! = PHY_INTERFACE_MODE_SGMII & &
phydev - > interface ! = PHY_INTERFACE_MODE_2500BASEX )
return - ENODEV ;
2019-03-24 11:08:13 +01:00
ret = aqr107_wait_reset_complete ( phydev ) ;
if ( ! ret )
aqr107_chip_info ( phydev ) ;
2019-02-22 23:48:14 +01:00
/* AQCS109 belongs to a chip family partially supporting 10G and 5G.
* PMA speed ability bits are the same for all members of the family ,
* AQCS109 however supports speeds up to 2.5 G only .
*/
2019-03-21 21:08:35 +01:00
ret = phy_set_max_speed ( phydev , SPEED_2500 ) ;
if ( ret )
return ret ;
/* ensure that a latched downshift event is cleared */
aqr107_read_downshift_event ( phydev ) ;
return aqr107_set_downshift ( phydev , MDIO_AN_VEND_PROV_DOWNSHIFT_DFLT ) ;
2019-02-22 23:48:14 +01:00
}
2019-03-24 11:04:21 +01:00
static void aqr107_link_change_notify ( struct phy_device * phydev )
{
u8 fw_major , fw_minor ;
bool downshift , short_reach , afr ;
2019-03-24 11:09:41 +01:00
int mode , val ;
2019-03-24 11:04:21 +01:00
if ( phydev - > state ! = PHY_RUNNING | | phydev - > autoneg = = AUTONEG_DISABLE )
return ;
val = phy_read_mmd ( phydev , MDIO_MMD_AN , MDIO_AN_RX_LP_STAT1 ) ;
/* call failed or link partner is no Aquantia PHY */
if ( val < 0 | | ! ( val & MDIO_AN_RX_LP_STAT1_AQ_PHY ) )
return ;
short_reach = val & MDIO_AN_RX_LP_STAT1_SHORT_REACH ;
downshift = val & MDIO_AN_RX_LP_STAT1_AQRATE_DOWNSHIFT ;
val = phy_read_mmd ( phydev , MDIO_MMD_AN , MDIO_AN_RX_LP_STAT4 ) ;
if ( val < 0 )
return ;
fw_major = FIELD_GET ( MDIO_AN_RX_LP_STAT4_FW_MAJOR , val ) ;
fw_minor = FIELD_GET ( MDIO_AN_RX_LP_STAT4_FW_MINOR , val ) ;
val = phy_read_mmd ( phydev , MDIO_MMD_AN , MDIO_AN_RX_VEND_STAT3 ) ;
if ( val < 0 )
return ;
afr = val & MDIO_AN_RX_VEND_STAT3_AFR ;
phydev_dbg ( phydev , " Link partner is Aquantia PHY, FW %u.%u%s%s%s \n " ,
fw_major , fw_minor ,
short_reach ? " , short reach mode " : " " ,
downshift ? " , fast-retrain downshift advertised " : " " ,
afr ? " , fast reframe advertised " : " " ) ;
2019-03-24 11:09:41 +01:00
val = phy_read_mmd ( phydev , MDIO_MMD_VEND1 , VEND1_GLOBAL_RSVD_STAT9 ) ;
if ( val < 0 )
return ;
mode = FIELD_GET ( VEND1_GLOBAL_RSVD_STAT9_MODE , val ) ;
if ( mode = = VEND1_GLOBAL_RSVD_STAT9_1000BT2 )
phydev_info ( phydev , " Aquantia 1000Base-T2 mode active \n " ) ;
2019-03-24 11:04:21 +01:00
}
2019-03-29 21:09:27 +01:00
static int aqr107_suspend ( struct phy_device * phydev )
{
return phy_set_bits_mmd ( phydev , MDIO_MMD_VEND1 , MDIO_CTRL1 ,
MDIO_CTRL1_LPOWER ) ;
}
static int aqr107_resume ( struct phy_device * phydev )
{
return phy_clear_bits_mmd ( phydev , MDIO_MMD_VEND1 , MDIO_CTRL1 ,
MDIO_CTRL1_LPOWER ) ;
}
2019-03-31 17:42:24 +02:00
static int aqr107_probe ( struct phy_device * phydev )
{
phydev - > priv = devm_kzalloc ( & phydev - > mdio . dev ,
sizeof ( struct aqr107_priv ) , GFP_KERNEL ) ;
if ( ! phydev - > priv )
return - ENOMEM ;
return aqr_hwmon_probe ( phydev ) ;
}
2019-02-03 21:16:18 +01:00
static struct phy_driver aqr_driver [ ] = {
2015-07-31 16:58:42 +08:00
{
2019-02-03 21:18:03 +01:00
PHY_ID_MATCH_MODEL ( PHY_ID_AQ1202 ) ,
2015-07-31 16:58:42 +08:00
. name = " Aquantia AQ1202 " ,
2019-02-03 21:16:18 +01:00
. config_aneg = aqr_config_aneg ,
. config_intr = aqr_config_intr ,
. ack_interrupt = aqr_ack_interrupt ,
. read_status = aqr_read_status ,
2015-07-31 16:58:42 +08:00
} ,
{
2019-02-03 21:18:03 +01:00
PHY_ID_MATCH_MODEL ( PHY_ID_AQ2104 ) ,
2015-07-31 16:58:42 +08:00
. name = " Aquantia AQ2104 " ,
2019-02-03 21:16:18 +01:00
. config_aneg = aqr_config_aneg ,
. config_intr = aqr_config_intr ,
. ack_interrupt = aqr_ack_interrupt ,
. read_status = aqr_read_status ,
2015-07-31 16:58:42 +08:00
} ,
{
2019-02-03 21:18:03 +01:00
PHY_ID_MATCH_MODEL ( PHY_ID_AQR105 ) ,
2015-07-31 16:58:42 +08:00
. name = " Aquantia AQR105 " ,
2019-02-03 21:16:18 +01:00
. config_aneg = aqr_config_aneg ,
. config_intr = aqr_config_intr ,
. ack_interrupt = aqr_ack_interrupt ,
. read_status = aqr_read_status ,
2015-07-31 16:58:42 +08:00
} ,
2016-10-20 16:30:31 +08:00
{
2019-02-03 21:18:03 +01:00
PHY_ID_MATCH_MODEL ( PHY_ID_AQR106 ) ,
2016-10-20 16:30:31 +08:00
. name = " Aquantia AQR106 " ,
2019-02-03 21:16:18 +01:00
. config_aneg = aqr_config_aneg ,
. config_intr = aqr_config_intr ,
. ack_interrupt = aqr_ack_interrupt ,
. read_status = aqr_read_status ,
2016-10-20 16:30:31 +08:00
} ,
{
2019-02-03 21:18:03 +01:00
PHY_ID_MATCH_MODEL ( PHY_ID_AQR107 ) ,
2016-10-20 16:30:31 +08:00
. name = " Aquantia AQR107 " ,
2019-03-31 17:42:24 +02:00
. probe = aqr107_probe ,
2019-03-19 23:04:38 +01:00
. config_init = aqr107_config_init ,
2019-02-03 21:16:18 +01:00
. config_aneg = aqr_config_aneg ,
. config_intr = aqr_config_intr ,
. ack_interrupt = aqr_ack_interrupt ,
2019-03-19 23:05:50 +01:00
. read_status = aqr107_read_status ,
2019-03-21 21:08:35 +01:00
. get_tunable = aqr107_get_tunable ,
. set_tunable = aqr107_set_tunable ,
2019-03-29 21:09:27 +01:00
. suspend = aqr107_suspend ,
. resume = aqr107_resume ,
2019-03-31 17:42:24 +02:00
. get_sset_count = aqr107_get_sset_count ,
. get_strings = aqr107_get_strings ,
. get_stats = aqr107_get_stats ,
2019-03-24 11:04:21 +01:00
. link_change_notify = aqr107_link_change_notify ,
2016-10-20 16:30:31 +08:00
} ,
2019-02-08 22:12:23 +01:00
{
PHY_ID_MATCH_MODEL ( PHY_ID_AQCS109 ) ,
. name = " Aquantia AQCS109 " ,
2019-03-31 17:42:24 +02:00
. probe = aqr107_probe ,
2019-02-22 23:48:14 +01:00
. config_init = aqcs109_config_init ,
2019-02-08 22:12:23 +01:00
. config_aneg = aqr_config_aneg ,
. config_intr = aqr_config_intr ,
. ack_interrupt = aqr_ack_interrupt ,
2019-03-19 23:05:50 +01:00
. read_status = aqr107_read_status ,
2019-03-21 21:08:35 +01:00
. get_tunable = aqr107_get_tunable ,
. set_tunable = aqr107_set_tunable ,
2019-03-29 21:09:27 +01:00
. suspend = aqr107_suspend ,
. resume = aqr107_resume ,
2019-03-31 17:42:24 +02:00
. get_sset_count = aqr107_get_sset_count ,
. get_strings = aqr107_get_strings ,
. get_stats = aqr107_get_stats ,
2019-03-24 11:04:21 +01:00
. link_change_notify = aqr107_link_change_notify ,
2019-02-08 22:12:23 +01:00
} ,
2015-07-31 16:58:42 +08:00
{
2019-02-03 21:18:03 +01:00
PHY_ID_MATCH_MODEL ( PHY_ID_AQR405 ) ,
2015-07-31 16:58:42 +08:00
. name = " Aquantia AQR405 " ,
2019-02-03 21:16:18 +01:00
. config_aneg = aqr_config_aneg ,
. config_intr = aqr_config_intr ,
. ack_interrupt = aqr_ack_interrupt ,
. read_status = aqr_read_status ,
2015-07-31 16:58:42 +08:00
} ,
} ;
2019-02-03 21:16:18 +01:00
module_phy_driver ( aqr_driver ) ;
2015-07-31 16:58:42 +08:00
2019-02-03 21:16:18 +01:00
static struct mdio_device_id __maybe_unused aqr_tbl [ ] = {
2019-02-03 21:18:03 +01:00
{ PHY_ID_MATCH_MODEL ( PHY_ID_AQ1202 ) } ,
{ PHY_ID_MATCH_MODEL ( PHY_ID_AQ2104 ) } ,
{ PHY_ID_MATCH_MODEL ( PHY_ID_AQR105 ) } ,
{ PHY_ID_MATCH_MODEL ( PHY_ID_AQR106 ) } ,
{ PHY_ID_MATCH_MODEL ( PHY_ID_AQR107 ) } ,
2019-02-08 22:12:23 +01:00
{ PHY_ID_MATCH_MODEL ( PHY_ID_AQCS109 ) } ,
2019-02-03 21:18:03 +01:00
{ PHY_ID_MATCH_MODEL ( PHY_ID_AQR405 ) } ,
2015-07-31 16:58:42 +08:00
{ }
} ;
2019-02-03 21:16:18 +01:00
MODULE_DEVICE_TABLE ( mdio , aqr_tbl ) ;
2015-07-31 16:58:42 +08:00
MODULE_DESCRIPTION ( " Aquantia PHY driver " ) ;
MODULE_AUTHOR ( " Shaohui Xie <Shaohui.Xie@freescale.com> " ) ;
MODULE_LICENSE ( " GPL v2 " ) ;