2016-07-07 19:08:55 -04:00
/*
* Driver for ( BCM4706 ) ? GBit MAC core on BCMA bus .
*
* Copyright ( C ) 2012 Rafał Miłecki < zajec5 @ gmail . com >
*
* Licensed under the GNU / GPL . See COPYING for details .
*/
# define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
# include <linux/bcma/bcma.h>
# include <linux/brcmphy.h>
# include "bgmac.h"
static bool bcma_mdio_wait_value ( struct bcma_device * core , u16 reg , u32 mask ,
u32 value , int timeout )
{
u32 val ;
int i ;
for ( i = 0 ; i < timeout / 10 ; i + + ) {
val = bcma_read32 ( core , reg ) ;
if ( ( val & mask ) = = value )
return true ;
udelay ( 10 ) ;
}
dev_err ( & core - > dev , " Timeout waiting for reg 0x%X \n " , reg ) ;
return false ;
}
/**************************************************
* PHY ops
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2017-01-31 19:37:55 +01:00
static u16 bcma_mdio_phy_read ( struct bgmac * bgmac , u8 phyaddr , u8 reg )
2016-07-07 19:08:55 -04:00
{
struct bcma_device * core ;
u16 phy_access_addr ;
u16 phy_ctl_addr ;
u32 tmp ;
BUILD_BUG_ON ( BGMAC_PA_DATA_MASK ! = BCMA_GMAC_CMN_PA_DATA_MASK ) ;
BUILD_BUG_ON ( BGMAC_PA_ADDR_MASK ! = BCMA_GMAC_CMN_PA_ADDR_MASK ) ;
BUILD_BUG_ON ( BGMAC_PA_ADDR_SHIFT ! = BCMA_GMAC_CMN_PA_ADDR_SHIFT ) ;
BUILD_BUG_ON ( BGMAC_PA_REG_MASK ! = BCMA_GMAC_CMN_PA_REG_MASK ) ;
BUILD_BUG_ON ( BGMAC_PA_REG_SHIFT ! = BCMA_GMAC_CMN_PA_REG_SHIFT ) ;
BUILD_BUG_ON ( BGMAC_PA_WRITE ! = BCMA_GMAC_CMN_PA_WRITE ) ;
BUILD_BUG_ON ( BGMAC_PA_START ! = BCMA_GMAC_CMN_PA_START ) ;
BUILD_BUG_ON ( BGMAC_PC_EPA_MASK ! = BCMA_GMAC_CMN_PC_EPA_MASK ) ;
BUILD_BUG_ON ( BGMAC_PC_MCT_MASK ! = BCMA_GMAC_CMN_PC_MCT_MASK ) ;
BUILD_BUG_ON ( BGMAC_PC_MCT_SHIFT ! = BCMA_GMAC_CMN_PC_MCT_SHIFT ) ;
BUILD_BUG_ON ( BGMAC_PC_MTE ! = BCMA_GMAC_CMN_PC_MTE ) ;
2017-01-31 19:37:55 +01:00
if ( bgmac - > bcma . core - > id . id = = BCMA_CORE_4706_MAC_GBIT ) {
core = bgmac - > bcma . core - > bus - > drv_gmac_cmn . core ;
2016-07-07 19:08:55 -04:00
phy_access_addr = BCMA_GMAC_CMN_PHY_ACCESS ;
phy_ctl_addr = BCMA_GMAC_CMN_PHY_CTL ;
} else {
2017-01-31 19:37:55 +01:00
core = bgmac - > bcma . core ;
2016-07-07 19:08:55 -04:00
phy_access_addr = BGMAC_PHY_ACCESS ;
phy_ctl_addr = BGMAC_PHY_CNTL ;
}
tmp = bcma_read32 ( core , phy_ctl_addr ) ;
tmp & = ~ BGMAC_PC_EPA_MASK ;
tmp | = phyaddr ;
bcma_write32 ( core , phy_ctl_addr , tmp ) ;
tmp = BGMAC_PA_START ;
tmp | = phyaddr < < BGMAC_PA_ADDR_SHIFT ;
tmp | = reg < < BGMAC_PA_REG_SHIFT ;
bcma_write32 ( core , phy_access_addr , tmp ) ;
if ( ! bcma_mdio_wait_value ( core , phy_access_addr , BGMAC_PA_START , 0 ,
1000 ) ) {
dev_err ( & core - > dev , " Reading PHY %d register 0x%X failed \n " ,
phyaddr , reg ) ;
return 0xffff ;
}
return bcma_read32 ( core , phy_access_addr ) & BGMAC_PA_DATA_MASK ;
}
/* http://bcm-v4.sipsolutions.net/mac-gbit/gmac/chipphywr */
2017-01-31 19:37:55 +01:00
static int bcma_mdio_phy_write ( struct bgmac * bgmac , u8 phyaddr , u8 reg ,
2016-07-07 19:08:55 -04:00
u16 value )
{
struct bcma_device * core ;
u16 phy_access_addr ;
u16 phy_ctl_addr ;
u32 tmp ;
2017-01-31 19:37:55 +01:00
if ( bgmac - > bcma . core - > id . id = = BCMA_CORE_4706_MAC_GBIT ) {
core = bgmac - > bcma . core - > bus - > drv_gmac_cmn . core ;
2016-07-07 19:08:55 -04:00
phy_access_addr = BCMA_GMAC_CMN_PHY_ACCESS ;
phy_ctl_addr = BCMA_GMAC_CMN_PHY_CTL ;
} else {
2017-01-31 19:37:55 +01:00
core = bgmac - > bcma . core ;
2016-07-07 19:08:55 -04:00
phy_access_addr = BGMAC_PHY_ACCESS ;
phy_ctl_addr = BGMAC_PHY_CNTL ;
}
tmp = bcma_read32 ( core , phy_ctl_addr ) ;
tmp & = ~ BGMAC_PC_EPA_MASK ;
tmp | = phyaddr ;
bcma_write32 ( core , phy_ctl_addr , tmp ) ;
2017-01-31 19:37:55 +01:00
bcma_write32 ( bgmac - > bcma . core , BGMAC_INT_STATUS , BGMAC_IS_MDIO ) ;
if ( bcma_read32 ( bgmac - > bcma . core , BGMAC_INT_STATUS ) & BGMAC_IS_MDIO )
2016-07-07 19:08:55 -04:00
dev_warn ( & core - > dev , " Error setting MDIO int \n " ) ;
tmp = BGMAC_PA_START ;
tmp | = BGMAC_PA_WRITE ;
tmp | = phyaddr < < BGMAC_PA_ADDR_SHIFT ;
tmp | = reg < < BGMAC_PA_REG_SHIFT ;
tmp | = value ;
bcma_write32 ( core , phy_access_addr , tmp ) ;
if ( ! bcma_mdio_wait_value ( core , phy_access_addr , BGMAC_PA_START , 0 ,
1000 ) ) {
dev_err ( & core - > dev , " Writing to PHY %d register 0x%X failed \n " ,
phyaddr , reg ) ;
return - ETIMEDOUT ;
}
return 0 ;
}
/* http://bcm-v4.sipsolutions.net/mac-gbit/gmac/chipphyinit */
2017-01-31 19:37:55 +01:00
static void bcma_mdio_phy_init ( struct bgmac * bgmac )
2016-07-07 19:08:55 -04:00
{
2017-01-31 19:37:55 +01:00
struct bcma_chipinfo * ci = & bgmac - > bcma . core - > bus - > chipinfo ;
2016-07-07 19:08:55 -04:00
u8 i ;
2017-01-31 19:37:56 +01:00
/* For some legacy hardware we do chipset-based PHY initialization here
* without even detecting PHY ID . It ' s hacky and should be cleaned as
* soon as someone can test it .
*/
2016-07-07 19:08:55 -04:00
if ( ci - > id = = BCMA_CHIP_ID_BCM5356 ) {
for ( i = 0 ; i < 5 ; i + + ) {
2017-01-31 19:37:55 +01:00
bcma_mdio_phy_write ( bgmac , i , 0x1f , 0x008b ) ;
bcma_mdio_phy_write ( bgmac , i , 0x15 , 0x0100 ) ;
bcma_mdio_phy_write ( bgmac , i , 0x1f , 0x000f ) ;
bcma_mdio_phy_write ( bgmac , i , 0x12 , 0x2aaa ) ;
bcma_mdio_phy_write ( bgmac , i , 0x1f , 0x000b ) ;
2016-07-07 19:08:55 -04:00
}
2017-01-31 19:37:56 +01:00
return ;
2016-07-07 19:08:55 -04:00
}
if ( ( ci - > id = = BCMA_CHIP_ID_BCM5357 & & ci - > pkg ! = 10 ) | |
( ci - > id = = BCMA_CHIP_ID_BCM4749 & & ci - > pkg ! = 10 ) | |
( ci - > id = = BCMA_CHIP_ID_BCM53572 & & ci - > pkg ! = 9 ) ) {
2017-01-31 19:37:55 +01:00
struct bcma_drv_cc * cc = & bgmac - > bcma . core - > bus - > drv_cc ;
2016-07-07 19:08:55 -04:00
bcma_chipco_chipctl_maskset ( cc , 2 , ~ 0xc0000000 , 0 ) ;
bcma_chipco_chipctl_maskset ( cc , 4 , ~ 0x80000000 , 0 ) ;
for ( i = 0 ; i < 5 ; i + + ) {
2017-01-31 19:37:55 +01:00
bcma_mdio_phy_write ( bgmac , i , 0x1f , 0x000f ) ;
bcma_mdio_phy_write ( bgmac , i , 0x16 , 0x5284 ) ;
bcma_mdio_phy_write ( bgmac , i , 0x1f , 0x000b ) ;
bcma_mdio_phy_write ( bgmac , i , 0x17 , 0x0010 ) ;
bcma_mdio_phy_write ( bgmac , i , 0x1f , 0x000f ) ;
bcma_mdio_phy_write ( bgmac , i , 0x16 , 0x5296 ) ;
bcma_mdio_phy_write ( bgmac , i , 0x17 , 0x1073 ) ;
bcma_mdio_phy_write ( bgmac , i , 0x17 , 0x9073 ) ;
bcma_mdio_phy_write ( bgmac , i , 0x16 , 0x52b6 ) ;
bcma_mdio_phy_write ( bgmac , i , 0x17 , 0x9273 ) ;
bcma_mdio_phy_write ( bgmac , i , 0x1f , 0x000b ) ;
2016-07-07 19:08:55 -04:00
}
2017-01-31 19:37:56 +01:00
return ;
2016-07-07 19:08:55 -04:00
}
2017-01-31 19:37:56 +01:00
/* For all other hw do initialization using PHY subsystem. */
if ( bgmac - > net_dev & & bgmac - > net_dev - > phydev )
phy_init_hw ( bgmac - > net_dev - > phydev ) ;
2016-07-07 19:08:55 -04:00
}
/* http://bcm-v4.sipsolutions.net/mac-gbit/gmac/chipphyreset */
static int bcma_mdio_phy_reset ( struct mii_bus * bus )
{
2017-01-31 19:37:55 +01:00
struct bgmac * bgmac = bus - > priv ;
u8 phyaddr = bgmac - > phyaddr ;
2016-07-07 19:08:55 -04:00
2017-01-31 19:37:55 +01:00
if ( phyaddr = = BGMAC_PHY_NOREGS )
2016-07-07 19:08:55 -04:00
return 0 ;
2017-01-31 19:37:55 +01:00
bcma_mdio_phy_write ( bgmac , phyaddr , MII_BMCR , BMCR_RESET ) ;
2016-07-07 19:08:55 -04:00
udelay ( 100 ) ;
2017-01-31 19:37:55 +01:00
if ( bcma_mdio_phy_read ( bgmac , phyaddr , MII_BMCR ) & BMCR_RESET )
dev_err ( bgmac - > dev , " PHY reset failed \n " ) ;
bcma_mdio_phy_init ( bgmac ) ;
2016-07-07 19:08:55 -04:00
return 0 ;
}
/**************************************************
* MII
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
static int bcma_mdio_mii_read ( struct mii_bus * bus , int mii_id , int regnum )
{
return bcma_mdio_phy_read ( bus - > priv , mii_id , regnum ) ;
}
static int bcma_mdio_mii_write ( struct mii_bus * bus , int mii_id , int regnum ,
u16 value )
{
return bcma_mdio_phy_write ( bus - > priv , mii_id , regnum , value ) ;
}
2017-01-31 19:37:55 +01:00
struct mii_bus * bcma_mdio_mii_register ( struct bgmac * bgmac )
2016-07-07 19:08:55 -04:00
{
2017-01-31 19:37:55 +01:00
struct bcma_device * core = bgmac - > bcma . core ;
2016-07-07 19:08:55 -04:00
struct mii_bus * mii_bus ;
int err ;
mii_bus = mdiobus_alloc ( ) ;
if ( ! mii_bus ) {
err = - ENOMEM ;
goto err ;
}
mii_bus - > name = " bcma_mdio mii bus " ;
sprintf ( mii_bus - > id , " %s-%d-%d " , " bcma_mdio " , core - > bus - > num ,
core - > core_unit ) ;
2017-01-31 19:37:55 +01:00
mii_bus - > priv = bgmac ;
2016-07-07 19:08:55 -04:00
mii_bus - > read = bcma_mdio_mii_read ;
mii_bus - > write = bcma_mdio_mii_write ;
mii_bus - > reset = bcma_mdio_phy_reset ;
mii_bus - > parent = & core - > dev ;
2017-01-31 19:37:55 +01:00
mii_bus - > phy_mask = ~ ( 1 < < bgmac - > phyaddr ) ;
2016-07-07 19:08:55 -04:00
err = mdiobus_register ( mii_bus ) ;
if ( err ) {
dev_err ( & core - > dev , " Registration of mii bus failed \n " ) ;
goto err_free_bus ;
}
return mii_bus ;
err_free_bus :
mdiobus_free ( mii_bus ) ;
err :
return ERR_PTR ( err ) ;
}
2016-07-07 19:08:57 -04:00
EXPORT_SYMBOL_GPL ( bcma_mdio_mii_register ) ;
2016-07-07 19:08:55 -04:00
void bcma_mdio_mii_unregister ( struct mii_bus * mii_bus )
{
if ( ! mii_bus )
return ;
mdiobus_unregister ( mii_bus ) ;
mdiobus_free ( mii_bus ) ;
}
2016-07-07 19:08:57 -04:00
EXPORT_SYMBOL_GPL ( bcma_mdio_mii_unregister ) ;
2016-07-07 19:08:55 -04:00
MODULE_AUTHOR ( " Rafał Miłecki " ) ;
MODULE_LICENSE ( " GPL " ) ;