2009-10-14 23:04:38 +04:00
/*
* This file is subject to the terms and conditions of the GNU General Public
* License . See the file " COPYING " in the main directory of this archive
* for more details .
*
2013-04-03 13:25:32 +04:00
* Copyright ( C ) 2009 - 2012 Cavium , Inc .
2009-10-14 23:04:38 +04:00
*/
# include <linux/platform_device.h>
2015-07-29 01:12:11 +03:00
# include <linux/of_address.h>
2012-07-05 20:12:39 +04:00
# include <linux/of_mdio.h>
# include <linux/delay.h>
# include <linux/module.h>
# include <linux/gfp.h>
2009-10-14 23:04:38 +04:00
# include <linux/phy.h>
2012-07-05 20:12:39 +04:00
# include <linux/io.h>
2009-10-14 23:04:38 +04:00
2015-07-29 01:12:11 +03:00
# ifdef CONFIG_CAVIUM_OCTEON_SOC
2009-10-14 23:04:38 +04:00
# include <asm/octeon/octeon.h>
2015-07-29 01:12:11 +03:00
# endif
2009-10-14 23:04:38 +04:00
2015-07-29 01:12:11 +03:00
# define DRV_VERSION "1.1"
# define DRV_DESCRIPTION "Cavium Networks Octeon / ThunderX SMI / MDIO driver"
2009-10-14 23:04:38 +04:00
2012-07-05 20:12:39 +04:00
# define SMI_CMD 0x0
# define SMI_WR_DAT 0x8
# define SMI_RD_DAT 0x10
# define SMI_CLK 0x18
# define SMI_EN 0x20
2015-07-29 01:12:11 +03:00
# ifdef __BIG_ENDIAN_BITFIELD
# define OCT_MDIO_BITFIELD_FIELD(field, more) \
field ; \
more
# else
# define OCT_MDIO_BITFIELD_FIELD(field, more) \
more \
field ;
# endif
union cvmx_smix_clk {
u64 u64 ;
struct cvmx_smix_clk_s {
OCT_MDIO_BITFIELD_FIELD ( u64 reserved_25_63 : 39 ,
OCT_MDIO_BITFIELD_FIELD ( u64 mode : 1 ,
OCT_MDIO_BITFIELD_FIELD ( u64 reserved_21_23 : 3 ,
OCT_MDIO_BITFIELD_FIELD ( u64 sample_hi : 5 ,
OCT_MDIO_BITFIELD_FIELD ( u64 sample_mode : 1 ,
OCT_MDIO_BITFIELD_FIELD ( u64 reserved_14_14 : 1 ,
OCT_MDIO_BITFIELD_FIELD ( u64 clk_idle : 1 ,
OCT_MDIO_BITFIELD_FIELD ( u64 preamble : 1 ,
OCT_MDIO_BITFIELD_FIELD ( u64 sample : 4 ,
OCT_MDIO_BITFIELD_FIELD ( u64 phase : 8 ,
; ) ) ) ) ) ) ) ) ) )
} s ;
} ;
union cvmx_smix_cmd {
u64 u64 ;
struct cvmx_smix_cmd_s {
OCT_MDIO_BITFIELD_FIELD ( u64 reserved_18_63 : 46 ,
OCT_MDIO_BITFIELD_FIELD ( u64 phy_op : 2 ,
OCT_MDIO_BITFIELD_FIELD ( u64 reserved_13_15 : 3 ,
OCT_MDIO_BITFIELD_FIELD ( u64 phy_adr : 5 ,
OCT_MDIO_BITFIELD_FIELD ( u64 reserved_5_7 : 3 ,
OCT_MDIO_BITFIELD_FIELD ( u64 reg_adr : 5 ,
; ) ) ) ) ) )
} s ;
} ;
union cvmx_smix_en {
u64 u64 ;
struct cvmx_smix_en_s {
OCT_MDIO_BITFIELD_FIELD ( u64 reserved_1_63 : 63 ,
OCT_MDIO_BITFIELD_FIELD ( u64 en : 1 ,
; ) )
} s ;
} ;
union cvmx_smix_rd_dat {
u64 u64 ;
struct cvmx_smix_rd_dat_s {
OCT_MDIO_BITFIELD_FIELD ( u64 reserved_18_63 : 46 ,
OCT_MDIO_BITFIELD_FIELD ( u64 pending : 1 ,
OCT_MDIO_BITFIELD_FIELD ( u64 val : 1 ,
OCT_MDIO_BITFIELD_FIELD ( u64 dat : 16 ,
; ) ) ) )
} s ;
} ;
union cvmx_smix_wr_dat {
u64 u64 ;
struct cvmx_smix_wr_dat_s {
OCT_MDIO_BITFIELD_FIELD ( u64 reserved_18_63 : 46 ,
OCT_MDIO_BITFIELD_FIELD ( u64 pending : 1 ,
OCT_MDIO_BITFIELD_FIELD ( u64 val : 1 ,
OCT_MDIO_BITFIELD_FIELD ( u64 dat : 16 ,
; ) ) ) )
} s ;
} ;
2013-04-03 13:25:32 +04:00
enum octeon_mdiobus_mode {
UNINIT = 0 ,
C22 ,
C45
} ;
2009-10-14 23:04:38 +04:00
struct octeon_mdiobus {
struct mii_bus * mii_bus ;
2012-07-05 20:12:39 +04:00
u64 register_base ;
resource_size_t mdio_phys ;
resource_size_t regsize ;
2013-04-03 13:25:32 +04:00
enum octeon_mdiobus_mode mode ;
2009-10-14 23:04:38 +04:00
int phy_irq [ PHY_MAX_ADDR ] ;
} ;
2015-07-29 01:12:11 +03:00
# ifdef CONFIG_CAVIUM_OCTEON_SOC
static void oct_mdio_writeq ( u64 val , u64 addr )
{
cvmx_write_csr ( addr , val ) ;
}
static u64 oct_mdio_readq ( u64 addr )
{
return cvmx_read_csr ( addr ) ;
}
# else
# define oct_mdio_writeq(val, addr) writeq_relaxed(val, (void *)addr)
# define oct_mdio_readq(addr) readq_relaxed((void *)addr)
# endif
2013-04-03 13:25:32 +04:00
static void octeon_mdiobus_set_mode ( struct octeon_mdiobus * p ,
enum octeon_mdiobus_mode m )
{
union cvmx_smix_clk smi_clk ;
if ( m = = p - > mode )
return ;
2015-07-29 01:12:11 +03:00
smi_clk . u64 = oct_mdio_readq ( p - > register_base + SMI_CLK ) ;
2013-04-03 13:25:32 +04:00
smi_clk . s . mode = ( m = = C45 ) ? 1 : 0 ;
smi_clk . s . preamble = 1 ;
2015-07-29 01:12:11 +03:00
oct_mdio_writeq ( smi_clk . u64 , p - > register_base + SMI_CLK ) ;
2013-04-03 13:25:32 +04:00
p - > mode = m ;
}
static int octeon_mdiobus_c45_addr ( struct octeon_mdiobus * p ,
int phy_id , int regnum )
{
union cvmx_smix_cmd smi_cmd ;
union cvmx_smix_wr_dat smi_wr ;
int timeout = 1000 ;
octeon_mdiobus_set_mode ( p , C45 ) ;
smi_wr . u64 = 0 ;
smi_wr . s . dat = regnum & 0xffff ;
2015-07-29 01:12:11 +03:00
oct_mdio_writeq ( smi_wr . u64 , p - > register_base + SMI_WR_DAT ) ;
2013-04-03 13:25:32 +04:00
regnum = ( regnum > > 16 ) & 0x1f ;
smi_cmd . u64 = 0 ;
smi_cmd . s . phy_op = 0 ; /* MDIO_CLAUSE_45_ADDRESS */
smi_cmd . s . phy_adr = phy_id ;
smi_cmd . s . reg_adr = regnum ;
2015-07-29 01:12:11 +03:00
oct_mdio_writeq ( smi_cmd . u64 , p - > register_base + SMI_CMD ) ;
2013-04-03 13:25:32 +04:00
do {
/* Wait 1000 clocks so we don't saturate the RSL bus
* doing reads .
*/
__delay ( 1000 ) ;
2015-07-29 01:12:11 +03:00
smi_wr . u64 = oct_mdio_readq ( p - > register_base + SMI_WR_DAT ) ;
2013-04-03 13:25:32 +04:00
} while ( smi_wr . s . pending & & - - timeout ) ;
if ( timeout < = 0 )
return - EIO ;
return 0 ;
}
2009-10-14 23:04:38 +04:00
static int octeon_mdiobus_read ( struct mii_bus * bus , int phy_id , int regnum )
{
struct octeon_mdiobus * p = bus - > priv ;
union cvmx_smix_cmd smi_cmd ;
union cvmx_smix_rd_dat smi_rd ;
2013-04-03 13:25:32 +04:00
unsigned int op = 1 ; /* MDIO_CLAUSE_22_READ */
2009-10-14 23:04:38 +04:00
int timeout = 1000 ;
2013-04-03 13:25:32 +04:00
if ( regnum & MII_ADDR_C45 ) {
int r = octeon_mdiobus_c45_addr ( p , phy_id , regnum ) ;
if ( r < 0 )
return r ;
regnum = ( regnum > > 16 ) & 0x1f ;
op = 3 ; /* MDIO_CLAUSE_45_READ */
} else {
octeon_mdiobus_set_mode ( p , C22 ) ;
}
2009-10-14 23:04:38 +04:00
smi_cmd . u64 = 0 ;
2013-04-03 13:25:32 +04:00
smi_cmd . s . phy_op = op ;
2009-10-14 23:04:38 +04:00
smi_cmd . s . phy_adr = phy_id ;
smi_cmd . s . reg_adr = regnum ;
2015-07-29 01:12:11 +03:00
oct_mdio_writeq ( smi_cmd . u64 , p - > register_base + SMI_CMD ) ;
2009-10-14 23:04:38 +04:00
do {
2013-04-03 13:25:32 +04:00
/* Wait 1000 clocks so we don't saturate the RSL bus
2009-10-14 23:04:38 +04:00
* doing reads .
*/
2012-07-05 20:12:39 +04:00
__delay ( 1000 ) ;
2015-07-29 01:12:11 +03:00
smi_rd . u64 = oct_mdio_readq ( p - > register_base + SMI_RD_DAT ) ;
2009-10-14 23:04:38 +04:00
} while ( smi_rd . s . pending & & - - timeout ) ;
if ( smi_rd . s . val )
return smi_rd . s . dat ;
else
return - EIO ;
}
static int octeon_mdiobus_write ( struct mii_bus * bus , int phy_id ,
int regnum , u16 val )
{
struct octeon_mdiobus * p = bus - > priv ;
union cvmx_smix_cmd smi_cmd ;
union cvmx_smix_wr_dat smi_wr ;
2013-04-03 13:25:32 +04:00
unsigned int op = 0 ; /* MDIO_CLAUSE_22_WRITE */
2009-10-14 23:04:38 +04:00
int timeout = 1000 ;
2013-04-03 13:25:32 +04:00
if ( regnum & MII_ADDR_C45 ) {
int r = octeon_mdiobus_c45_addr ( p , phy_id , regnum ) ;
if ( r < 0 )
return r ;
regnum = ( regnum > > 16 ) & 0x1f ;
op = 1 ; /* MDIO_CLAUSE_45_WRITE */
} else {
octeon_mdiobus_set_mode ( p , C22 ) ;
}
2009-10-14 23:04:38 +04:00
smi_wr . u64 = 0 ;
smi_wr . s . dat = val ;
2015-07-29 01:12:11 +03:00
oct_mdio_writeq ( smi_wr . u64 , p - > register_base + SMI_WR_DAT ) ;
2009-10-14 23:04:38 +04:00
smi_cmd . u64 = 0 ;
2013-04-03 13:25:32 +04:00
smi_cmd . s . phy_op = op ;
2009-10-14 23:04:38 +04:00
smi_cmd . s . phy_adr = phy_id ;
smi_cmd . s . reg_adr = regnum ;
2015-07-29 01:12:11 +03:00
oct_mdio_writeq ( smi_cmd . u64 , p - > register_base + SMI_CMD ) ;
2009-10-14 23:04:38 +04:00
do {
2013-04-03 13:25:32 +04:00
/* Wait 1000 clocks so we don't saturate the RSL bus
2009-10-14 23:04:38 +04:00
* doing reads .
*/
2012-07-05 20:12:39 +04:00
__delay ( 1000 ) ;
2015-07-29 01:12:11 +03:00
smi_wr . u64 = oct_mdio_readq ( p - > register_base + SMI_WR_DAT ) ;
2009-10-14 23:04:38 +04:00
} while ( smi_wr . s . pending & & - - timeout ) ;
if ( timeout < = 0 )
return - EIO ;
return 0 ;
}
2012-12-03 18:24:14 +04:00
static int octeon_mdiobus_probe ( struct platform_device * pdev )
2009-10-14 23:04:38 +04:00
{
struct octeon_mdiobus * bus ;
2012-07-05 20:12:39 +04:00
struct resource * res_mem ;
2010-04-02 05:17:54 +04:00
union cvmx_smix_en smi_en ;
2009-10-14 23:04:38 +04:00
int err = - ENOENT ;
bus = devm_kzalloc ( & pdev - > dev , sizeof ( * bus ) , GFP_KERNEL ) ;
if ( ! bus )
return - ENOMEM ;
2012-07-05 20:12:39 +04:00
res_mem = platform_get_resource ( pdev , IORESOURCE_MEM , 0 ) ;
if ( res_mem = = NULL ) {
dev_err ( & pdev - > dev , " found no memory resource \n " ) ;
2015-07-29 01:12:12 +03:00
return - ENXIO ;
2012-07-05 20:12:39 +04:00
}
2015-07-29 01:12:12 +03:00
2012-07-05 20:12:39 +04:00
bus - > mdio_phys = res_mem - > start ;
bus - > regsize = resource_size ( res_mem ) ;
2015-07-29 01:12:12 +03:00
2012-07-05 20:12:39 +04:00
if ( ! devm_request_mem_region ( & pdev - > dev , bus - > mdio_phys , bus - > regsize ,
res_mem - > name ) ) {
dev_err ( & pdev - > dev , " request_mem_region failed \n " ) ;
2015-07-29 01:12:12 +03:00
return - ENXIO ;
2012-07-05 20:12:39 +04:00
}
2015-07-29 01:12:12 +03:00
2012-07-05 20:12:39 +04:00
bus - > register_base =
( u64 ) devm_ioremap ( & pdev - > dev , bus - > mdio_phys , bus - > regsize ) ;
2015-07-29 01:12:12 +03:00
if ( ! bus - > register_base ) {
dev_err ( & pdev - > dev , " dev_ioremap failed \n " ) ;
return - ENOMEM ;
}
2009-10-14 23:04:38 +04:00
bus - > mii_bus = mdiobus_alloc ( ) ;
if ( ! bus - > mii_bus )
2012-07-05 20:12:39 +04:00
goto fail ;
2009-10-14 23:04:38 +04:00
2010-04-02 05:17:54 +04:00
smi_en . u64 = 0 ;
smi_en . s . en = 1 ;
2015-07-29 01:12:11 +03:00
oct_mdio_writeq ( smi_en . u64 , bus - > register_base + SMI_EN ) ;
2009-10-14 23:04:38 +04:00
bus - > mii_bus - > priv = bus ;
bus - > mii_bus - > irq = bus - > phy_irq ;
bus - > mii_bus - > name = " mdio-octeon " ;
2012-07-05 20:12:39 +04:00
snprintf ( bus - > mii_bus - > id , MII_BUS_ID_SIZE , " %llx " , bus - > register_base ) ;
2009-10-14 23:04:38 +04:00
bus - > mii_bus - > parent = & pdev - > dev ;
bus - > mii_bus - > read = octeon_mdiobus_read ;
bus - > mii_bus - > write = octeon_mdiobus_write ;
2013-08-21 14:15:15 +04:00
platform_set_drvdata ( pdev , bus ) ;
2009-10-14 23:04:38 +04:00
2012-07-05 20:12:39 +04:00
err = of_mdiobus_register ( bus - > mii_bus , pdev - > dev . of_node ) ;
2009-10-14 23:04:38 +04:00
if ( err )
2012-07-05 20:12:39 +04:00
goto fail_register ;
2009-10-14 23:04:38 +04:00
dev_info ( & pdev - > dev , " Version " DRV_VERSION " \n " ) ;
return 0 ;
2012-07-05 20:12:39 +04:00
fail_register :
2009-10-14 23:04:38 +04:00
mdiobus_free ( bus - > mii_bus ) ;
2012-07-05 20:12:39 +04:00
fail :
2010-04-02 05:17:54 +04:00
smi_en . u64 = 0 ;
2015-07-29 01:12:11 +03:00
oct_mdio_writeq ( smi_en . u64 , bus - > register_base + SMI_EN ) ;
2009-10-14 23:04:38 +04:00
return err ;
}
2012-12-03 18:24:14 +04:00
static int octeon_mdiobus_remove ( struct platform_device * pdev )
2009-10-14 23:04:38 +04:00
{
struct octeon_mdiobus * bus ;
2010-04-02 05:17:54 +04:00
union cvmx_smix_en smi_en ;
2009-10-14 23:04:38 +04:00
2013-09-02 12:10:09 +04:00
bus = platform_get_drvdata ( pdev ) ;
2009-10-14 23:04:38 +04:00
mdiobus_unregister ( bus - > mii_bus ) ;
mdiobus_free ( bus - > mii_bus ) ;
2010-04-02 05:17:54 +04:00
smi_en . u64 = 0 ;
2015-07-29 01:12:11 +03:00
oct_mdio_writeq ( smi_en . u64 , bus - > register_base + SMI_EN ) ;
2009-10-14 23:04:38 +04:00
return 0 ;
}
2015-03-17 21:40:23 +03:00
static const struct of_device_id octeon_mdiobus_match [ ] = {
2012-07-05 20:12:39 +04:00
{
. compatible = " cavium,octeon-3860-mdio " ,
} ,
{ } ,
} ;
MODULE_DEVICE_TABLE ( of , octeon_mdiobus_match ) ;
2009-10-14 23:04:38 +04:00
static struct platform_driver octeon_mdiobus_driver = {
. driver = {
. name = " mdio-octeon " ,
2012-07-05 20:12:39 +04:00
. of_match_table = octeon_mdiobus_match ,
2009-10-14 23:04:38 +04:00
} ,
. probe = octeon_mdiobus_probe ,
2012-12-03 18:24:14 +04:00
. remove = octeon_mdiobus_remove ,
2009-10-14 23:04:38 +04:00
} ;
void octeon_mdiobus_force_mod_depencency ( void )
{
/* Let ethernet drivers force us to be loaded. */
}
EXPORT_SYMBOL ( octeon_mdiobus_force_mod_depencency ) ;
2013-03-20 05:41:32 +04:00
module_platform_driver ( octeon_mdiobus_driver ) ;
2009-10-14 23:04:38 +04:00
MODULE_DESCRIPTION ( DRV_DESCRIPTION ) ;
MODULE_VERSION ( DRV_VERSION ) ;
MODULE_AUTHOR ( " David Daney " ) ;
MODULE_LICENSE ( " GPL " ) ;