2005-10-11 03:58:14 +04:00
/*
* drivers / net / ibm_emac / ibm_emac_rgmii . c
*
* Driver for PowerPC 4 xx on - chip ethernet controller , RGMII bridge support .
*
* Copyright ( c ) 2004 , 2005 Zultys Technologies .
* Eugene Surovegin < eugene . surovegin @ zultys . com > or < ebs @ ebshome . net >
*
* Based on original work by
* Matt Porter < mporter @ kernel . crashing . org >
* Copyright 2004 MontaVista Software , Inc .
*
* 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/kernel.h>
# include <linux/ethtool.h>
# include <asm/io.h>
# include "ibm_emac_core.h"
# include "ibm_emac_debug.h"
/* RGMIIx_FER */
# define RGMII_FER_MASK(idx) (0x7 << ((idx) * 4))
# define RGMII_FER_RTBI(idx) (0x4 << ((idx) * 4))
# define RGMII_FER_RGMII(idx) (0x5 << ((idx) * 4))
# define RGMII_FER_TBI(idx) (0x6 << ((idx) * 4))
# define RGMII_FER_GMII(idx) (0x7 << ((idx) * 4))
/* RGMIIx_SSR */
# define RGMII_SSR_MASK(idx) (0x7 << ((idx) * 8))
# define RGMII_SSR_100(idx) (0x2 << ((idx) * 8))
# define RGMII_SSR_1000(idx) (0x4 << ((idx) * 8))
/* RGMII bridge supports only GMII/TBI and RGMII/RTBI PHYs */
static inline int rgmii_valid_mode ( int phy_mode )
{
return phy_mode = = PHY_MODE_GMII | |
phy_mode = = PHY_MODE_RGMII | |
phy_mode = = PHY_MODE_TBI | |
phy_mode = = PHY_MODE_RTBI ;
}
static inline const char * rgmii_mode_name ( int mode )
{
switch ( mode ) {
case PHY_MODE_RGMII :
return " RGMII " ;
case PHY_MODE_TBI :
return " TBI " ;
case PHY_MODE_GMII :
return " GMII " ;
case PHY_MODE_RTBI :
return " RTBI " ;
default :
BUG ( ) ;
}
}
static inline u32 rgmii_mode_mask ( int mode , int input )
{
switch ( mode ) {
case PHY_MODE_RGMII :
return RGMII_FER_RGMII ( input ) ;
case PHY_MODE_TBI :
return RGMII_FER_TBI ( input ) ;
case PHY_MODE_GMII :
return RGMII_FER_GMII ( input ) ;
case PHY_MODE_RTBI :
return RGMII_FER_RTBI ( input ) ;
default :
BUG ( ) ;
}
}
static int __init rgmii_init ( struct ocp_device * ocpdev , int input , int mode )
{
struct ibm_ocp_rgmii * dev = ocp_get_drvdata ( ocpdev ) ;
struct rgmii_regs * p ;
RGMII_DBG ( " %d: init(%d, %d) " NL , ocpdev - > def - > index , input , mode ) ;
if ( ! dev ) {
dev = kzalloc ( sizeof ( struct ibm_ocp_rgmii ) , GFP_KERNEL ) ;
if ( ! dev ) {
printk ( KERN_ERR
" rgmii%d: couldn't allocate device structure! \n " ,
ocpdev - > def - > index ) ;
return - ENOMEM ;
}
p = ( struct rgmii_regs * ) ioremap ( ocpdev - > def - > paddr ,
sizeof ( struct rgmii_regs ) ) ;
if ( ! p ) {
printk ( KERN_ERR
" rgmii%d: could not ioremap device registers! \n " ,
ocpdev - > def - > index ) ;
kfree ( dev ) ;
return - ENOMEM ;
}
dev - > base = p ;
ocp_set_drvdata ( ocpdev , dev ) ;
/* Disable all inputs by default */
out_be32 ( & p - > fer , 0 ) ;
} else
p = dev - > base ;
/* Enable this input */
out_be32 ( & p - > fer , in_be32 ( & p - > fer ) | rgmii_mode_mask ( mode , input ) ) ;
printk ( KERN_NOTICE " rgmii%d: input %d in %s mode \n " ,
ocpdev - > def - > index , input , rgmii_mode_name ( mode ) ) ;
+ + dev - > users ;
return 0 ;
}
int __init rgmii_attach ( void * emac )
{
struct ocp_enet_private * dev = emac ;
struct ocp_func_emac_data * emacdata = dev - > def - > additions ;
/* Check if we need to attach to a RGMII */
if ( emacdata - > rgmii_idx > = 0 & & rgmii_valid_mode ( emacdata - > phy_mode ) ) {
dev - > rgmii_input = emacdata - > rgmii_mux ;
dev - > rgmii_dev =
ocp_find_device ( OCP_VENDOR_IBM , OCP_FUNC_RGMII ,
emacdata - > rgmii_idx ) ;
if ( ! dev - > rgmii_dev ) {
printk ( KERN_ERR " emac%d: unknown rgmii%d! \n " ,
dev - > def - > index , emacdata - > rgmii_idx ) ;
return - ENODEV ;
}
if ( rgmii_init
( dev - > rgmii_dev , dev - > rgmii_input , emacdata - > phy_mode ) ) {
printk ( KERN_ERR
" emac%d: rgmii%d initialization failed! \n " ,
dev - > def - > index , emacdata - > rgmii_idx ) ;
return - ENODEV ;
}
}
return 0 ;
}
void rgmii_set_speed ( struct ocp_device * ocpdev , int input , int speed )
{
struct ibm_ocp_rgmii * dev = ocp_get_drvdata ( ocpdev ) ;
u32 ssr = in_be32 ( & dev - > base - > ssr ) & ~ RGMII_SSR_MASK ( input ) ;
RGMII_DBG ( " %d: speed(%d, %d) " NL , ocpdev - > def - > index , input , speed ) ;
if ( speed = = SPEED_1000 )
ssr | = RGMII_SSR_1000 ( input ) ;
else if ( speed = = SPEED_100 )
ssr | = RGMII_SSR_100 ( input ) ;
out_be32 ( & dev - > base - > ssr , ssr ) ;
}
2007-05-16 22:57:37 +04:00
void __rgmii_fini ( struct ocp_device * ocpdev , int input )
2005-10-11 03:58:14 +04:00
{
struct ibm_ocp_rgmii * dev = ocp_get_drvdata ( ocpdev ) ;
BUG_ON ( ! dev | | dev - > users = = 0 ) ;
RGMII_DBG ( " %d: fini(%d) " NL , ocpdev - > def - > index , input ) ;
/* Disable this input */
out_be32 ( & dev - > base - > fer ,
in_be32 ( & dev - > base - > fer ) & ~ RGMII_FER_MASK ( input ) ) ;
if ( ! - - dev - > users ) {
/* Free everything if this is the last user */
ocp_set_drvdata ( ocpdev , NULL ) ;
iounmap ( ( void * ) dev - > base ) ;
kfree ( dev ) ;
}
}
int __rgmii_get_regs_len ( struct ocp_device * ocpdev )
{
return sizeof ( struct emac_ethtool_regs_subhdr ) +
sizeof ( struct rgmii_regs ) ;
}
void * rgmii_dump_regs ( struct ocp_device * ocpdev , void * buf )
{
struct ibm_ocp_rgmii * dev = ocp_get_drvdata ( ocpdev ) ;
struct emac_ethtool_regs_subhdr * hdr = buf ;
struct rgmii_regs * regs = ( struct rgmii_regs * ) ( hdr + 1 ) ;
hdr - > version = 0 ;
hdr - > index = ocpdev - > def - > index ;
memcpy_fromio ( regs , dev - > base , sizeof ( struct rgmii_regs ) ) ;
return regs + 1 ;
}