2019-01-21 21:34:53 +03:00
// SPDX-License-Identifier: GPL-2.0
2016-03-11 20:53:10 +03:00
/*
* Copyright ( C ) 2009 - 2016 Cavium , Inc .
*/
# include <linux/delay.h>
2021-03-15 13:49:05 +03:00
# include <linux/io.h>
2016-03-11 20:53:10 +03:00
# include <linux/module.h>
# include <linux/phy.h>
# include "mdio-cavium.h"
static void cavium_mdiobus_set_mode ( struct cavium_mdiobus * p ,
enum cavium_mdiobus_mode m )
{
union cvmx_smix_clk smi_clk ;
if ( m = = p - > mode )
return ;
smi_clk . u64 = oct_mdio_readq ( p - > register_base + SMI_CLK ) ;
smi_clk . s . mode = ( m = = C45 ) ? 1 : 0 ;
smi_clk . s . preamble = 1 ;
oct_mdio_writeq ( smi_clk . u64 , p - > register_base + SMI_CLK ) ;
p - > mode = m ;
}
static int cavium_mdiobus_c45_addr ( struct cavium_mdiobus * p ,
2023-01-12 18:15:07 +03:00
int phy_id , int devad , int regnum )
2016-03-11 20:53:10 +03:00
{
union cvmx_smix_cmd smi_cmd ;
union cvmx_smix_wr_dat smi_wr ;
int timeout = 1000 ;
cavium_mdiobus_set_mode ( p , C45 ) ;
smi_wr . u64 = 0 ;
smi_wr . s . dat = regnum & 0xffff ;
oct_mdio_writeq ( smi_wr . u64 , p - > register_base + SMI_WR_DAT ) ;
smi_cmd . u64 = 0 ;
smi_cmd . s . phy_op = 0 ; /* MDIO_CLAUSE_45_ADDRESS */
smi_cmd . s . phy_adr = phy_id ;
2023-01-12 18:15:07 +03:00
smi_cmd . s . reg_adr = devad ;
2016-03-11 20:53:10 +03:00
oct_mdio_writeq ( smi_cmd . u64 , p - > register_base + SMI_CMD ) ;
do {
/* Wait 1000 clocks so we don't saturate the RSL bus
* doing reads .
*/
__delay ( 1000 ) ;
smi_wr . u64 = oct_mdio_readq ( p - > register_base + SMI_WR_DAT ) ;
} while ( smi_wr . s . pending & & - - timeout ) ;
if ( timeout < = 0 )
return - EIO ;
return 0 ;
}
2023-01-12 18:15:07 +03:00
int cavium_mdiobus_read_c22 ( struct mii_bus * bus , int phy_id , int regnum )
2016-03-11 20:53:10 +03:00
{
struct cavium_mdiobus * p = bus - > priv ;
union cvmx_smix_cmd smi_cmd ;
union cvmx_smix_rd_dat smi_rd ;
int timeout = 1000 ;
2023-01-12 18:15:07 +03:00
cavium_mdiobus_set_mode ( p , C22 ) ;
smi_cmd . u64 = 0 ;
2023-01-15 19:42:03 +03:00
smi_cmd . s . phy_op = 1 ; /* MDIO_CLAUSE_22_READ */
2023-01-12 18:15:07 +03:00
smi_cmd . s . phy_adr = phy_id ;
smi_cmd . s . reg_adr = regnum ;
oct_mdio_writeq ( smi_cmd . u64 , p - > register_base + SMI_CMD ) ;
do {
/* Wait 1000 clocks so we don't saturate the RSL bus
* doing reads .
*/
__delay ( 1000 ) ;
smi_rd . u64 = oct_mdio_readq ( p - > register_base + SMI_RD_DAT ) ;
} while ( smi_rd . s . pending & & - - timeout ) ;
if ( smi_rd . s . val )
return smi_rd . s . dat ;
else
return - EIO ;
}
EXPORT_SYMBOL ( cavium_mdiobus_read_c22 ) ;
2016-03-11 20:53:10 +03:00
2023-01-12 18:15:07 +03:00
int cavium_mdiobus_read_c45 ( struct mii_bus * bus , int phy_id , int devad ,
int regnum )
{
struct cavium_mdiobus * p = bus - > priv ;
union cvmx_smix_cmd smi_cmd ;
union cvmx_smix_rd_dat smi_rd ;
int timeout = 1000 ;
int r ;
2016-03-11 20:53:10 +03:00
2023-01-12 18:15:07 +03:00
r = cavium_mdiobus_c45_addr ( p , phy_id , devad , regnum ) ;
if ( r < 0 )
return r ;
2016-03-11 20:53:10 +03:00
smi_cmd . u64 = 0 ;
2023-01-12 18:15:07 +03:00
smi_cmd . s . phy_op = 3 ; /* MDIO_CLAUSE_45_READ */
2016-03-11 20:53:10 +03:00
smi_cmd . s . phy_adr = phy_id ;
smi_cmd . s . reg_adr = regnum ;
oct_mdio_writeq ( smi_cmd . u64 , p - > register_base + SMI_CMD ) ;
do {
/* Wait 1000 clocks so we don't saturate the RSL bus
* doing reads .
*/
__delay ( 1000 ) ;
smi_rd . u64 = oct_mdio_readq ( p - > register_base + SMI_RD_DAT ) ;
} while ( smi_rd . s . pending & & - - timeout ) ;
if ( smi_rd . s . val )
return smi_rd . s . dat ;
else
return - EIO ;
}
2023-01-12 18:15:07 +03:00
EXPORT_SYMBOL ( cavium_mdiobus_read_c45 ) ;
2016-03-11 20:53:10 +03:00
2023-01-12 18:15:07 +03:00
int cavium_mdiobus_write_c22 ( struct mii_bus * bus , int phy_id , int regnum ,
u16 val )
2016-03-11 20:53:10 +03:00
{
struct cavium_mdiobus * p = bus - > priv ;
union cvmx_smix_cmd smi_cmd ;
union cvmx_smix_wr_dat smi_wr ;
int timeout = 1000 ;
2023-01-12 18:15:07 +03:00
cavium_mdiobus_set_mode ( p , C22 ) ;
2016-03-11 20:53:10 +03:00
2023-01-12 18:15:07 +03:00
smi_wr . u64 = 0 ;
smi_wr . s . dat = val ;
oct_mdio_writeq ( smi_wr . u64 , p - > register_base + SMI_WR_DAT ) ;
2016-03-11 20:53:10 +03:00
2023-01-12 18:15:07 +03:00
smi_cmd . u64 = 0 ;
2023-01-15 19:42:03 +03:00
smi_cmd . s . phy_op = 0 ; /* MDIO_CLAUSE_22_WRITE */
2023-01-12 18:15:07 +03:00
smi_cmd . s . phy_adr = phy_id ;
smi_cmd . s . reg_adr = regnum ;
oct_mdio_writeq ( smi_cmd . u64 , p - > register_base + SMI_CMD ) ;
do {
/* Wait 1000 clocks so we don't saturate the RSL bus
* doing reads .
*/
__delay ( 1000 ) ;
smi_wr . u64 = oct_mdio_readq ( p - > register_base + SMI_WR_DAT ) ;
} while ( smi_wr . s . pending & & - - timeout ) ;
if ( timeout < = 0 )
return - EIO ;
return 0 ;
}
EXPORT_SYMBOL ( cavium_mdiobus_write_c22 ) ;
int cavium_mdiobus_write_c45 ( struct mii_bus * bus , int phy_id , int devad ,
int regnum , u16 val )
{
struct cavium_mdiobus * p = bus - > priv ;
union cvmx_smix_cmd smi_cmd ;
union cvmx_smix_wr_dat smi_wr ;
int timeout = 1000 ;
int r ;
r = cavium_mdiobus_c45_addr ( p , phy_id , devad , regnum ) ;
if ( r < 0 )
return r ;
2016-03-11 20:53:10 +03:00
smi_wr . u64 = 0 ;
smi_wr . s . dat = val ;
oct_mdio_writeq ( smi_wr . u64 , p - > register_base + SMI_WR_DAT ) ;
smi_cmd . u64 = 0 ;
2023-01-12 18:15:07 +03:00
smi_cmd . s . phy_op = 1 ; /* MDIO_CLAUSE_45_WRITE */
2016-03-11 20:53:10 +03:00
smi_cmd . s . phy_adr = phy_id ;
2023-01-12 18:15:07 +03:00
smi_cmd . s . reg_adr = devad ;
2016-03-11 20:53:10 +03:00
oct_mdio_writeq ( smi_cmd . u64 , p - > register_base + SMI_CMD ) ;
do {
/* Wait 1000 clocks so we don't saturate the RSL bus
* doing reads .
*/
__delay ( 1000 ) ;
smi_wr . u64 = oct_mdio_readq ( p - > register_base + SMI_WR_DAT ) ;
} while ( smi_wr . s . pending & & - - timeout ) ;
if ( timeout < = 0 )
return - EIO ;
return 0 ;
}
2023-01-12 18:15:07 +03:00
EXPORT_SYMBOL ( cavium_mdiobus_write_c45 ) ;
2016-03-15 03:30:37 +03:00
MODULE_DESCRIPTION ( " Common code for OCTEON and Thunder MDIO bus drivers " ) ;
MODULE_AUTHOR ( " David Daney " ) ;
2019-01-21 21:34:53 +03:00
MODULE_LICENSE ( " GPL v2 " ) ;