2014-02-13 16:08:45 -08:00
/*
* Broadcom BCM7xxx internal transceivers support .
*
* Copyright ( C ) 2014 , Broadcom Corporation
*
* 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/module.h>
# include <linux/phy.h>
# include <linux/delay.h>
2015-10-06 12:25:48 -07:00
# include "bcm-phy-lib.h"
2014-02-13 16:08:45 -08:00
# include <linux/bitops.h>
# include <linux/brcmphy.h>
2014-08-22 18:55:45 -07:00
# include <linux/mdio.h>
2014-02-13 16:08:45 -08:00
/* Broadcom BCM7xxx internal PHY registers */
/* 40nm only register definitions */
# define MII_BCM7XXX_100TX_AUX_CTL 0x10
# define MII_BCM7XXX_100TX_FALSE_CAR 0x13
# define MII_BCM7XXX_100TX_DISC 0x14
# define MII_BCM7XXX_AUX_MODE 0x1d
# define MII_BCM7XX_64CLK_MDIO BIT(12)
# define MII_BCM7XXX_TEST 0x1f
# define MII_BCM7XXX_SHD_MODE_2 BIT(2)
2014-03-24 16:36:47 -07:00
/* 28nm only register definitions */
# define MISC_ADDR(base, channel) base, channel
# define DSP_TAP10 MISC_ADDR(0x0a, 0)
# define PLL_PLLCTRL_1 MISC_ADDR(0x32, 1)
# define PLL_PLLCTRL_2 MISC_ADDR(0x32, 2)
# define PLL_PLLCTRL_4 MISC_ADDR(0x33, 0)
# define AFE_RXCONFIG_0 MISC_ADDR(0x38, 0)
# define AFE_RXCONFIG_1 MISC_ADDR(0x38, 1)
2014-11-11 14:55:13 -08:00
# define AFE_RXCONFIG_2 MISC_ADDR(0x38, 2)
2014-03-24 16:36:47 -07:00
# define AFE_RX_LP_COUNTER MISC_ADDR(0x38, 3)
# define AFE_TX_CONFIG MISC_ADDR(0x39, 0)
2014-11-11 14:55:13 -08:00
# define AFE_VDCA_ICTRL_0 MISC_ADDR(0x39, 1)
# define AFE_VDAC_OTHERS_0 MISC_ADDR(0x39, 3)
2014-03-24 16:36:47 -07:00
# define AFE_HPF_TRIM_OTHERS MISC_ADDR(0x3a, 0)
2014-11-11 14:55:12 -08:00
static void r_rc_cal_reset ( struct phy_device * phydev )
{
/* Reset R_CAL/RC_CAL Engine */
2015-10-06 12:25:48 -07:00
bcm_phy_write_exp ( phydev , 0x00b0 , 0x0010 ) ;
2014-11-11 14:55:12 -08:00
/* Disable Reset R_AL/RC_CAL Engine */
2015-10-06 12:25:48 -07:00
bcm_phy_write_exp ( phydev , 0x00b0 , 0x0000 ) ;
2014-11-11 14:55:12 -08:00
}
2014-11-11 14:55:11 -08:00
static int bcm7xxx_28nm_b0_afe_config_init ( struct phy_device * phydev )
2014-02-13 16:08:45 -08:00
{
/* Increase VCO range to prevent unlocking problem of PLL at low
* temp
*/
2015-10-06 12:25:48 -07:00
bcm_phy_write_misc ( phydev , PLL_PLLCTRL_1 , 0x0048 ) ;
2014-02-13 16:08:45 -08:00
/* Change Ki to 011 */
2015-10-06 12:25:48 -07:00
bcm_phy_write_misc ( phydev , PLL_PLLCTRL_2 , 0x021b ) ;
2014-02-13 16:08:45 -08:00
/* Disable loading of TVCO buffer to bandgap, set bandgap trim
* to 111
*/
2015-10-06 12:25:48 -07:00
bcm_phy_write_misc ( phydev , PLL_PLLCTRL_4 , 0x0e20 ) ;
2014-02-13 16:08:45 -08:00
/* Adjust bias current trim by -3 */
2015-10-06 12:25:48 -07:00
bcm_phy_write_misc ( phydev , DSP_TAP10 , 0x690b ) ;
2014-02-13 16:08:45 -08:00
/* Switch to CORE_BASE1E */
2015-10-06 12:25:50 -07:00
phy_write ( phydev , MII_BRCM_CORE_BASE1E , 0xd ) ;
2014-02-13 16:08:45 -08:00
2014-11-11 14:55:12 -08:00
r_rc_cal_reset ( phydev ) ;
2014-02-13 16:08:45 -08:00
2014-03-24 16:36:48 -07:00
/* write AFE_RXCONFIG_0 */
2015-10-06 12:25:48 -07:00
bcm_phy_write_misc ( phydev , AFE_RXCONFIG_0 , 0xeb19 ) ;
2014-03-24 16:36:48 -07:00
/* write AFE_RXCONFIG_1 */
2015-10-06 12:25:48 -07:00
bcm_phy_write_misc ( phydev , AFE_RXCONFIG_1 , 0x9a3f ) ;
2014-03-24 16:36:48 -07:00
/* write AFE_RX_LP_COUNTER */
2015-10-06 12:25:48 -07:00
bcm_phy_write_misc ( phydev , AFE_RX_LP_COUNTER , 0x7fc0 ) ;
2014-03-24 16:36:48 -07:00
/* write AFE_HPF_TRIM_OTHERS */
2015-10-06 12:25:48 -07:00
bcm_phy_write_misc ( phydev , AFE_HPF_TRIM_OTHERS , 0x000b ) ;
2014-03-24 16:36:48 -07:00
/* write AFTE_TX_CONFIG */
2015-10-06 12:25:48 -07:00
bcm_phy_write_misc ( phydev , AFE_TX_CONFIG , 0x0800 ) ;
2014-03-24 16:36:48 -07:00
2014-02-13 16:08:45 -08:00
return 0 ;
}
2014-11-11 14:55:13 -08:00
static int bcm7xxx_28nm_d0_afe_config_init ( struct phy_device * phydev )
{
/* AFE_RXCONFIG_0 */
2015-10-06 12:25:48 -07:00
bcm_phy_write_misc ( phydev , AFE_RXCONFIG_0 , 0xeb15 ) ;
2014-11-11 14:55:13 -08:00
/* AFE_RXCONFIG_1 */
2015-10-06 12:25:48 -07:00
bcm_phy_write_misc ( phydev , AFE_RXCONFIG_1 , 0x9b2f ) ;
2014-11-11 14:55:13 -08:00
/* AFE_RXCONFIG_2, set rCal offset for HT=0 code and LT=-2 code */
2015-10-06 12:25:48 -07:00
bcm_phy_write_misc ( phydev , AFE_RXCONFIG_2 , 0x2003 ) ;
2014-11-11 14:55:13 -08:00
/* AFE_RX_LP_COUNTER, set RX bandwidth to maximum */
2015-10-06 12:25:48 -07:00
bcm_phy_write_misc ( phydev , AFE_RX_LP_COUNTER , 0x7fc0 ) ;
2014-11-11 14:55:13 -08:00
2015-06-08 11:05:20 -07:00
/* AFE_TX_CONFIG, set 100BT Cfeed=011 to improve rise/fall time */
2015-10-06 12:25:48 -07:00
bcm_phy_write_misc ( phydev , AFE_TX_CONFIG , 0x431 ) ;
2014-11-11 14:55:13 -08:00
/* AFE_VDCA_ICTRL_0, set Iq=1101 instead of 0111 for AB symmetry */
2015-10-06 12:25:48 -07:00
bcm_phy_write_misc ( phydev , AFE_VDCA_ICTRL_0 , 0xa7da ) ;
2014-11-11 14:55:13 -08:00
/* AFE_VDAC_OTHERS_0, set 1000BT Cidac=010 for all ports */
2015-10-06 12:25:48 -07:00
bcm_phy_write_misc ( phydev , AFE_VDAC_OTHERS_0 , 0xa020 ) ;
2014-11-11 14:55:13 -08:00
/* AFE_HPF_TRIM_OTHERS, set 100Tx/10BT to -4.5% swing and set rCal
* offset for HT = 0 code
*/
2015-10-06 12:25:48 -07:00
bcm_phy_write_misc ( phydev , AFE_HPF_TRIM_OTHERS , 0x00e3 ) ;
2014-11-11 14:55:13 -08:00
/* CORE_BASE1E, force trim to overwrite and set I_ext trim to 0000 */
2015-10-06 12:25:50 -07:00
phy_write ( phydev , MII_BRCM_CORE_BASE1E , 0x0010 ) ;
2014-11-11 14:55:13 -08:00
/* DSP_TAP10, adjust bias current trim (+0% swing, +0 tick) */
2015-10-06 12:25:48 -07:00
bcm_phy_write_misc ( phydev , DSP_TAP10 , 0x011b ) ;
2014-11-11 14:55:13 -08:00
/* Reset R_CAL/RC_CAL engine */
r_rc_cal_reset ( phydev ) ;
return 0 ;
}
2014-11-11 14:55:14 -08:00
static int bcm7xxx_28nm_e0_plus_afe_config_init ( struct phy_device * phydev )
{
/* AFE_RXCONFIG_1, provide more margin for INL/DNL measurement */
2015-10-06 12:25:48 -07:00
bcm_phy_write_misc ( phydev , AFE_RXCONFIG_1 , 0x9b2f ) ;
2014-11-11 14:55:14 -08:00
2015-06-08 11:05:20 -07:00
/* AFE_TX_CONFIG, set 100BT Cfeed=011 to improve rise/fall time */
2015-10-06 12:25:48 -07:00
bcm_phy_write_misc ( phydev , AFE_TX_CONFIG , 0x431 ) ;
2015-06-08 11:05:20 -07:00
2014-11-11 14:55:14 -08:00
/* AFE_VDCA_ICTRL_0, set Iq=1101 instead of 0111 for AB symmetry */
2015-10-06 12:25:48 -07:00
bcm_phy_write_misc ( phydev , AFE_VDCA_ICTRL_0 , 0xa7da ) ;
2014-11-11 14:55:14 -08:00
/* AFE_HPF_TRIM_OTHERS, set 100Tx/10BT to -4.5% swing and set rCal
* offset for HT = 0 code
*/
2015-10-06 12:25:48 -07:00
bcm_phy_write_misc ( phydev , AFE_HPF_TRIM_OTHERS , 0x00e3 ) ;
2014-11-11 14:55:14 -08:00
/* CORE_BASE1E, force trim to overwrite and set I_ext trim to 0000 */
2015-10-06 12:25:50 -07:00
phy_write ( phydev , MII_BRCM_CORE_BASE1E , 0x0010 ) ;
2014-11-11 14:55:14 -08:00
/* DSP_TAP10, adjust bias current trim (+0% swing, +0 tick) */
2015-10-06 12:25:48 -07:00
bcm_phy_write_misc ( phydev , DSP_TAP10 , 0x011b ) ;
2014-11-11 14:55:14 -08:00
/* Reset R_CAL/RC_CAL engine */
r_rc_cal_reset ( phydev ) ;
return 0 ;
}
2014-02-13 16:08:45 -08:00
static int bcm7xxx_28nm_config_init ( struct phy_device * phydev )
{
2014-09-19 13:07:56 -07:00
u8 rev = PHY_BRCM_7XXX_REV ( phydev - > dev_flags ) ;
u8 patch = PHY_BRCM_7XXX_PATCH ( phydev - > dev_flags ) ;
int ret = 0 ;
2014-11-11 14:55:10 -08:00
pr_info_once ( " %s: %s PHY revision: 0x%02x, patch: %d \n " ,
2016-01-06 20:11:10 +01:00
phydev_name ( phydev ) , phydev - > drv - > name , rev , patch ) ;
2014-09-19 13:07:56 -07:00
2015-06-26 10:39:04 -07:00
/* Dummy read to a register to workaround an issue upon reset where the
* internal inverter may not allow the first MDIO transaction to pass
* the MDIO management controller and make us return 0xffff for such
* reads .
*/
phy_read ( phydev , MII_BMSR ) ;
2014-09-19 13:07:56 -07:00
switch ( rev ) {
case 0xb0 :
2014-11-11 14:55:11 -08:00
ret = bcm7xxx_28nm_b0_afe_config_init ( phydev ) ;
2014-09-19 13:07:56 -07:00
break ;
2014-11-11 14:55:13 -08:00
case 0xd0 :
ret = bcm7xxx_28nm_d0_afe_config_init ( phydev ) ;
break ;
2014-11-11 14:55:14 -08:00
case 0xe0 :
case 0xf0 :
2014-12-03 09:57:00 -08:00
/* Rev G0 introduces a roll over */
case 0x10 :
2014-11-11 14:55:14 -08:00
ret = bcm7xxx_28nm_e0_plus_afe_config_init ( phydev ) ;
break ;
2014-09-19 13:07:56 -07:00
default :
break ;
}
2014-02-13 16:08:45 -08:00
2014-08-22 18:55:41 -07:00
if ( ret )
return ret ;
2015-10-06 12:25:48 -07:00
ret = bcm_phy_enable_eee ( phydev ) ;
2014-08-22 18:55:45 -07:00
if ( ret )
return ret ;
2015-10-06 12:25:48 -07:00
return bcm_phy_enable_apd ( phydev , true ) ;
2014-02-13 16:08:45 -08:00
}
2014-08-14 16:52:52 -07:00
static int bcm7xxx_28nm_resume ( struct phy_device * phydev )
{
int ret ;
/* Re-apply workarounds coming out suspend/resume */
ret = bcm7xxx_28nm_config_init ( phydev ) ;
if ( ret )
return ret ;
/* 28nm Gigabit PHYs come out of reset without any half-duplex
* or " hub " compliant advertised mode , fix that . This does not
* cause any problems with the PHY library since genphy_config_aneg ( )
* gracefully handles auto - negotiated and forced modes .
*/
return genphy_config_aneg ( phydev ) ;
}
2014-02-13 16:08:45 -08:00
static int phy_set_clr_bits ( struct phy_device * dev , int location ,
int set_mask , int clr_mask )
{
int v , ret ;
v = phy_read ( dev , location ) ;
if ( v < 0 )
return v ;
v & = ~ clr_mask ;
v | = set_mask ;
ret = phy_write ( dev , location , v ) ;
if ( ret < 0 )
return ret ;
return v ;
}
static int bcm7xxx_config_init ( struct phy_device * phydev )
{
int ret ;
/* Enable 64 clock MDIO */
phy_write ( phydev , MII_BCM7XXX_AUX_MODE , MII_BCM7XX_64CLK_MDIO ) ;
phy_read ( phydev , MII_BCM7XXX_AUX_MODE ) ;
2014-09-19 13:07:51 -07:00
/* Workaround only required for 100Mbits/sec capable PHYs */
if ( phydev - > supported & PHY_GBIT_FEATURES )
2014-02-13 16:08:45 -08:00
return 0 ;
/* set shadow mode 2 */
ret = phy_set_clr_bits ( phydev , MII_BCM7XXX_TEST ,
MII_BCM7XXX_SHD_MODE_2 , MII_BCM7XXX_SHD_MODE_2 ) ;
if ( ret < 0 )
return ret ;
/* set iddq_clkbias */
phy_write ( phydev , MII_BCM7XXX_100TX_DISC , 0x0F00 ) ;
udelay ( 10 ) ;
/* reset iddq_clkbias */
phy_write ( phydev , MII_BCM7XXX_100TX_DISC , 0x0C00 ) ;
phy_write ( phydev , MII_BCM7XXX_100TX_FALSE_CAR , 0x7555 ) ;
/* reset shadow mode 2 */
ret = phy_set_clr_bits ( phydev , MII_BCM7XXX_TEST , MII_BCM7XXX_SHD_MODE_2 , 0 ) ;
if ( ret < 0 )
return ret ;
return 0 ;
}
/* Workaround for putting the PHY in IDDQ mode, required
2014-08-14 16:52:53 -07:00
* for all BCM7XXX 40 nm and 65 nm PHYs
2014-02-13 16:08:45 -08:00
*/
static int bcm7xxx_suspend ( struct phy_device * phydev )
{
int ret ;
const struct bcm7xxx_regs {
int reg ;
u16 value ;
} bcm7xxx_suspend_cfg [ ] = {
{ MII_BCM7XXX_TEST , 0x008b } ,
{ MII_BCM7XXX_100TX_AUX_CTL , 0x01c0 } ,
{ MII_BCM7XXX_100TX_DISC , 0x7000 } ,
{ MII_BCM7XXX_TEST , 0x000f } ,
{ MII_BCM7XXX_100TX_AUX_CTL , 0x20d0 } ,
{ MII_BCM7XXX_TEST , 0x000b } ,
} ;
unsigned int i ;
for ( i = 0 ; i < ARRAY_SIZE ( bcm7xxx_suspend_cfg ) ; i + + ) {
ret = phy_write ( phydev ,
bcm7xxx_suspend_cfg [ i ] . reg ,
bcm7xxx_suspend_cfg [ i ] . value ) ;
if ( ret )
return ret ;
}
return 0 ;
}
static int bcm7xxx_dummy_config_init ( struct phy_device * phydev )
{
return 0 ;
}
2014-08-26 13:15:24 -07:00
# define BCM7XXX_28NM_GPHY(_oui, _name) \
{ \
. phy_id = ( _oui ) , \
. phy_id_mask = 0xfffffff0 , \
. name = _name , \
. features = PHY_GBIT_FEATURES | \
SUPPORTED_Pause | SUPPORTED_Asym_Pause , \
. flags = PHY_IS_INTERNAL , \
2014-11-11 14:55:11 -08:00
. config_init = bcm7xxx_28nm_config_init , \
2014-08-26 13:15:24 -07:00
. config_aneg = genphy_config_aneg , \
. read_status = genphy_read_status , \
. resume = bcm7xxx_28nm_resume , \
}
2014-02-13 16:08:45 -08:00
static struct phy_driver bcm7xxx_driver [ ] = {
2014-08-26 13:15:27 -07:00
BCM7XXX_28NM_GPHY ( PHY_ID_BCM7250 , " Broadcom BCM7250 " ) ,
BCM7XXX_28NM_GPHY ( PHY_ID_BCM7364 , " Broadcom BCM7364 " ) ,
2014-08-26 13:15:24 -07:00
BCM7XXX_28NM_GPHY ( PHY_ID_BCM7366 , " Broadcom BCM7366 " ) ,
BCM7XXX_28NM_GPHY ( PHY_ID_BCM7439 , " Broadcom BCM7439 " ) ,
2015-03-09 15:44:13 -07:00
BCM7XXX_28NM_GPHY ( PHY_ID_BCM7439_2 , " Broadcom BCM7439 (2) " ) ,
2014-08-26 13:15:24 -07:00
BCM7XXX_28NM_GPHY ( PHY_ID_BCM7445 , " Broadcom BCM7445 " ) ,
2014-02-13 16:08:45 -08:00
{
2014-10-01 11:58:02 -07:00
. phy_id = PHY_ID_BCM7425 ,
. phy_id_mask = 0xfffffff0 ,
. name = " Broadcom BCM7425 " ,
. features = PHY_GBIT_FEATURES |
SUPPORTED_Pause | SUPPORTED_Asym_Pause ,
2015-05-22 14:07:30 -07:00
. flags = PHY_IS_INTERNAL ,
2014-10-01 11:58:02 -07:00
. config_init = bcm7xxx_config_init ,
. config_aneg = genphy_config_aneg ,
. read_status = genphy_read_status ,
. suspend = bcm7xxx_suspend ,
. resume = bcm7xxx_config_init ,
} , {
. phy_id = PHY_ID_BCM7429 ,
. phy_id_mask = 0xfffffff0 ,
. name = " Broadcom BCM7429 " ,
. features = PHY_GBIT_FEATURES |
SUPPORTED_Pause | SUPPORTED_Asym_Pause ,
. flags = PHY_IS_INTERNAL ,
. config_init = bcm7xxx_config_init ,
. config_aneg = genphy_config_aneg ,
. read_status = genphy_read_status ,
. suspend = bcm7xxx_suspend ,
. resume = bcm7xxx_config_init ,
2015-11-24 15:30:21 -08:00
} , {
. phy_id = PHY_ID_BCM7435 ,
. phy_id_mask = 0xfffffff0 ,
. name = " Broadcom BCM7435 " ,
. features = PHY_GBIT_FEATURES |
SUPPORTED_Pause | SUPPORTED_Asym_Pause ,
. flags = PHY_IS_INTERNAL ,
. config_init = bcm7xxx_config_init ,
. config_aneg = genphy_config_aneg ,
. read_status = genphy_read_status ,
. suspend = bcm7xxx_suspend ,
. resume = bcm7xxx_config_init ,
2014-10-01 11:58:02 -07:00
} , {
2014-02-13 16:08:45 -08:00
. phy_id = PHY_BCM_OUI_4 ,
. phy_id_mask = 0xffff0000 ,
. name = " Broadcom BCM7XXX 40nm " ,
. features = PHY_GBIT_FEATURES |
SUPPORTED_Pause | SUPPORTED_Asym_Pause ,
. flags = PHY_IS_INTERNAL ,
. config_init = bcm7xxx_config_init ,
. config_aneg = genphy_config_aneg ,
. read_status = genphy_read_status ,
. suspend = bcm7xxx_suspend ,
. resume = bcm7xxx_config_init ,
} , {
. phy_id = PHY_BCM_OUI_5 ,
. phy_id_mask = 0xffffff00 ,
. name = " Broadcom BCM7XXX 65nm " ,
. features = PHY_BASIC_FEATURES |
SUPPORTED_Pause | SUPPORTED_Asym_Pause ,
. flags = PHY_IS_INTERNAL ,
. config_init = bcm7xxx_dummy_config_init ,
. config_aneg = genphy_config_aneg ,
. read_status = genphy_read_status ,
. suspend = bcm7xxx_suspend ,
. resume = bcm7xxx_config_init ,
} } ;
static struct mdio_device_id __maybe_unused bcm7xxx_tbl [ ] = {
2014-08-26 13:15:27 -07:00
{ PHY_ID_BCM7250 , 0xfffffff0 , } ,
{ PHY_ID_BCM7364 , 0xfffffff0 , } ,
2014-02-13 16:08:45 -08:00
{ PHY_ID_BCM7366 , 0xfffffff0 , } ,
2014-10-01 11:58:02 -07:00
{ PHY_ID_BCM7425 , 0xfffffff0 , } ,
{ PHY_ID_BCM7429 , 0xfffffff0 , } ,
2014-02-13 16:08:45 -08:00
{ PHY_ID_BCM7439 , 0xfffffff0 , } ,
2015-11-24 15:30:21 -08:00
{ PHY_ID_BCM7435 , 0xfffffff0 , } ,
2014-02-13 16:08:45 -08:00
{ PHY_ID_BCM7445 , 0xfffffff0 , } ,
{ PHY_BCM_OUI_4 , 0xffff0000 } ,
{ PHY_BCM_OUI_5 , 0xffffff00 } ,
{ }
} ;
2014-11-11 19:45:59 +01:00
module_phy_driver ( bcm7xxx_driver ) ;
2014-02-13 16:08:45 -08:00
MODULE_DEVICE_TABLE ( mdio , bcm7xxx_tbl ) ;
MODULE_DESCRIPTION ( " Broadcom BCM7xxx internal PHY driver " ) ;
MODULE_LICENSE ( " GPL " ) ;
MODULE_AUTHOR ( " Broadcom Corporation " ) ;